Merge remote-tracking branch 'origin/feature/mall_product' into feature/mall_product

# Conflicts:
#	yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/api/order/TradeOrderApi.java
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/api/order/TradeOrderApiImpl.java
This commit is contained in:
owen 2023-10-09 23:33:51 +08:00
commit 74a5e40d8d
59 changed files with 1166 additions and 497 deletions

View File

@ -240,7 +240,8 @@ public class CollectionUtils {
return valueFunc.apply(t);
}
public static <T, V extends Comparable<? super V>> V getSumValue(List<T> from, Function<T, V> valueFunc, BinaryOperator<V> accumulator) {
public static <T, V extends Comparable<? super V>> V getSumValue(List<T> from, Function<T, V> valueFunc,
BinaryOperator<V> accumulator) {
if (CollUtil.isEmpty(from)) {
return null;
}

View File

@ -56,7 +56,7 @@ public interface BaseMapperX<T> extends MPJBaseMapper<T> {
}
default Long selectCount() {
return selectCount(new QueryWrapper<T>());
return selectCount(new QueryWrapper<>());
}
default Long selectCount(String field, Object value) {

View File

@ -4,7 +4,6 @@ import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCr
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationValidateJoinRespDTO;
import javax.validation.Valid;
import java.time.LocalDateTime;
// TODO @芋艿后面也再撸撸这几个接口
@ -18,19 +17,21 @@ public interface CombinationRecordApi {
/**
* 校验是否满足拼团条件
*
* @param activityId 活动编号
* @param userId 用户编号
* @param activityId 活动编号
* @param headId 团长编号
* @param skuId sku 编号
* @param count 数量
*/
void validateCombinationRecord(Long activityId, Long userId, Long skuId, Integer count);
void validateCombinationRecord(Long userId, Long activityId, Long headId, Long skuId, Integer count);
/**
* 创建开团记录
*
* @param reqDTO 请求 DTO
* @return 开团记录编号
*/
void createCombinationRecord(@Valid CombinationRecordCreateReqDTO reqDTO);
Long createCombinationRecord(@Valid CombinationRecordCreateReqDTO reqDTO);
/**
* 查询拼团记录是否成功
@ -41,14 +42,6 @@ public interface CombinationRecordApi {
*/
boolean isCombinationRecordSuccess(Long userId, Long orderId);
/**
* 更新拼团状态为成功
*
* @param userId 用户编号
* @param orderId 订单编号
*/
void updateRecordStatusToSuccess(Long userId, Long orderId);
/**
* 更新拼团状态为失败
*
@ -57,26 +50,19 @@ public interface CombinationRecordApi {
*/
void updateRecordStatusToFailed(Long userId, Long orderId);
/**
* 更新拼团状态为 进行中
*
* @param userId 用户编号
* @param orderId 订单编号
* @param startTime 开始时间
*/
void updateRecordStatusToInProgress(Long userId, Long orderId, LocalDateTime startTime);
/**
* 下单前校验是否满足拼团活动条件
*
* 如果校验失败则抛出业务异常
*
* @param activityId 活动编号
* @param userId 用户编号
* @param activityId 活动编号
* @param headId 团长编号
* @param skuId sku 编号
* @param count 数量
* @return 拼团信息
*/
CombinationValidateJoinRespDTO validateJoinCombination(Long activityId, Long userId, Long skuId, Integer count);
CombinationValidateJoinRespDTO validateJoinCombination(Long userId, Long activityId, Long headId,
Long skuId, Integer count);
}

View File

@ -4,7 +4,6 @@ import lombok.Data;
import javax.validation.constraints.NotNull;
// TODO @芋艿这块要在看看
/**
* 拼团记录的创建 Request DTO
*
@ -46,7 +45,6 @@ public class CombinationRecordCreateReqDTO {
/**
* 团长编号
*/
@NotNull(message = "团长编号不能为空")
private Long headId;
/**
* 拼团商品单价

View File

@ -80,7 +80,7 @@ public interface ErrorCodeConstants {
ErrorCode COMBINATION_RECORD_EXISTS = new ErrorCode(1_013_011_001, "拼团失败,已参与过该拼团");
ErrorCode COMBINATION_RECORD_HEAD_NOT_EXISTS = new ErrorCode(1_013_011_002, "拼团失败,父拼团不存在");
ErrorCode COMBINATION_RECORD_USER_FULL = new ErrorCode(1_013_011_003, "拼团失败,拼团人数已满");
ErrorCode COMBINATION_RECORD_FAILED_HAVE_JOINED = new ErrorCode(1_013_011_004, "拼团失败,已参与其它拼团");
ErrorCode COMBINATION_RECORD_FAILED_HAVE_JOINED = new ErrorCode(1_013_011_004, "拼团失败,原因:存在该活动正在进行的拼团记录");
ErrorCode COMBINATION_RECORD_FAILED_TIME_NOT_START = new ErrorCode(1_013_011_005, "拼团失败,活动未开始");
ErrorCode COMBINATION_RECORD_FAILED_TIME_END = new ErrorCode(1_013_011_006, "拼团失败,活动已经结束");
ErrorCode COMBINATION_RECORD_FAILED_SINGLE_LIMIT_COUNT_EXCEED = new ErrorCode(1_013_011_007, "拼团失败,原因:单次限购超出");

View File

@ -40,4 +40,12 @@ public enum CombinationRecordStatusEnum implements IntArrayValuable {
return ObjectUtil.equal(status, SUCCESS.getStatus());
}
public static boolean isInProgress(Integer status) {
return ObjectUtil.equal(status, IN_PROGRESS.getStatus());
}
public static boolean isFailed(Integer status) {
return ObjectUtil.equal(status, FAILED.getStatus());
}
}

View File

@ -2,12 +2,15 @@ package cn.iocoder.yudao.module.promotion.api.combination;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationValidateJoinRespDTO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
import cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COMBINATION_RECORD_NOT_EXISTS;
/**
* 拼团活动 API 实现类
@ -21,23 +24,22 @@ public class CombinationRecordApiImpl implements CombinationRecordApi {
private CombinationRecordService recordService;
@Override
public void validateCombinationRecord(Long activityId, Long userId, Long skuId, Integer count) {
recordService.validateCombinationRecord(activityId, userId, skuId, count);
public void validateCombinationRecord(Long userId, Long activityId, Long headId, Long skuId, Integer count) {
recordService.validateCombinationRecord(userId, activityId, headId, skuId, count);
}
@Override
public void createCombinationRecord(CombinationRecordCreateReqDTO reqDTO) {
recordService.createCombinationRecord(reqDTO);
public Long createCombinationRecord(CombinationRecordCreateReqDTO reqDTO) {
return recordService.createCombinationRecord(reqDTO);
}
@Override
public boolean isCombinationRecordSuccess(Long userId, Long orderId) {
return CombinationRecordStatusEnum.isSuccess(recordService.getCombinationRecord(userId, orderId).getStatus());
}
@Override
public void updateRecordStatusToSuccess(Long userId, Long orderId) {
recordService.updateCombinationRecordStatusByUserIdAndOrderId(CombinationRecordStatusEnum.SUCCESS.getStatus(), userId, orderId);
CombinationRecordDO record = recordService.getCombinationRecord(userId, orderId);
if (record == null) {
throw exception(COMBINATION_RECORD_NOT_EXISTS);
}
return CombinationRecordStatusEnum.isSuccess(record.getStatus());
}
@Override
@ -46,14 +48,8 @@ public class CombinationRecordApiImpl implements CombinationRecordApi {
}
@Override
public void updateRecordStatusToInProgress(Long userId, Long orderId, LocalDateTime startTime) {
recordService.updateRecordStatusAndStartTimeByUserIdAndOrderId(CombinationRecordStatusEnum.IN_PROGRESS.getStatus(),
userId, orderId, startTime);
}
@Override
public CombinationValidateJoinRespDTO validateJoinCombination(Long activityId, Long userId, Long skuId, Integer count) {
return recordService.validateJoinCombination(activityId, userId, skuId, count);
public CombinationValidateJoinRespDTO validateJoinCombination(Long userId, Long activityId, Long headId, Long skuId, Integer count) {
return recordService.validateJoinCombination(userId, activityId, headId, skuId, count);
}
}

View File

@ -5,14 +5,14 @@ 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.product.api.spu.dto.ProductSpuRespDTO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityCreateReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityPageReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityRespVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityUpdateReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.*;
import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
import cn.iocoder.yudao.module.promotion.service.combination.CombinationActivityService;
import cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
@ -23,6 +23,8 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static cn.hutool.core.collection.CollectionUtil.newArrayList;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@ -36,6 +38,8 @@ public class CombinationActivityController {
@Resource
private CombinationActivityService combinationActivityService;
@Resource
private CombinationRecordService combinationRecordService;
@Resource
private ProductSpuApi productSpuApi;
@ -70,14 +74,14 @@ public class CombinationActivityController {
@PreAuthorize("@ss.hasPermission('promotion:combination-activity:query')")
public CommonResult<CombinationActivityRespVO> getCombinationActivity(@RequestParam("id") Long id) {
CombinationActivityDO activity = combinationActivityService.getCombinationActivity(id);
List<CombinationProductDO> products = combinationActivityService.getCombinationProductsByActivityIds(newArrayList(id));
List<CombinationProductDO> products = combinationActivityService.getCombinationProductListByActivityIds(newArrayList(id));
return success(CombinationActivityConvert.INSTANCE.convert(activity, products));
}
@GetMapping("/page")
@Operation(summary = "获得拼团活动分页")
@PreAuthorize("@ss.hasPermission('promotion:combination-activity:query')")
public CommonResult<PageResult<CombinationActivityRespVO>> getCombinationActivityPage(
public CommonResult<PageResult<CombinationActivityPageItemRespVO>> getCombinationActivityPage(
@Valid CombinationActivityPageReqVO pageVO) {
// 查询拼团活动
PageResult<CombinationActivityDO> pageResult = combinationActivityService.getCombinationActivityPage(pageVO);
@ -85,12 +89,21 @@ public class CombinationActivityController {
return success(PageResult.empty(pageResult.getTotal()));
}
// 统计数据
Set<Long> activityIds = convertSet(pageResult.getList(), CombinationActivityDO::getId);
Map<Long, Integer> groupCountMap = combinationRecordService.getCombinationRecordCountMapByActivity(
activityIds, null, CombinationRecordDO.HEAD_ID_GROUP);
Map<Long, Integer> groupSuccessCountMap = combinationRecordService.getCombinationRecordCountMapByActivity(
activityIds, CombinationRecordStatusEnum.SUCCESS.getStatus(), CombinationRecordDO.HEAD_ID_GROUP);
Map<Long, Integer> recordCountMap = combinationRecordService.getCombinationRecordCountMapByActivity(
activityIds, null, null);
// 拼接数据
List<CombinationProductDO> products = combinationActivityService.getCombinationProductsByActivityIds(
List<CombinationProductDO> products = combinationActivityService.getCombinationProductListByActivityIds(
convertSet(pageResult.getList(), CombinationActivityDO::getId));
List<ProductSpuRespDTO> spus = productSpuApi.getSpuList(
convertSet(pageResult.getList(), CombinationActivityDO::getSpuId));
return success(CombinationActivityConvert.INSTANCE.convertPage(pageResult, products, spus));
return success(CombinationActivityConvert.INSTANCE.convertPage(pageResult, products,
groupCountMap, groupSuccessCountMap, recordCountMap, spus));
}
}

View File

@ -0,0 +1,64 @@
package cn.iocoder.yudao.module.promotion.controller.admin.combination;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordPageItemRespVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordReqPageVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordSummaryVO;
import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
import cn.iocoder.yudao.module.promotion.service.combination.CombinationActivityService;
import cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
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/combination-record")
@Validated
public class CombinationRecordController {
@Resource
private CombinationActivityService combinationActivityService;
@Resource
@Lazy
private CombinationRecordService combinationRecordService;
@GetMapping("/page")
@Operation(summary = "获得拼团记录分页")
@PreAuthorize("@ss.hasPermission('promotion:combination-record:query')")
public CommonResult<PageResult<CombinationRecordPageItemRespVO>> getBargainRecordPage(@Valid CombinationRecordReqPageVO pageVO) {
PageResult<CombinationRecordDO> recordPage = combinationRecordService.getCombinationRecordPage(pageVO);
List<CombinationActivityDO> activities = combinationActivityService.getCombinationActivityListByIds(
convertSet(recordPage.getList(), CombinationRecordDO::getActivityId));
return success(CombinationActivityConvert.INSTANCE.convert(recordPage, activities));
}
@GetMapping("/get-summary")
@Operation(summary = "获得拼团记录的概要信息", description = "用于拼团记录页面展示")
@PreAuthorize("@ss.hasPermission('promotion:combination-record:query')")
public CommonResult<CombinationRecordSummaryVO> getCombinationRecordSummary() {
CombinationRecordSummaryVO summaryVO = new CombinationRecordSummaryVO();
summaryVO.setUserCount(combinationRecordService.getCombinationRecordCount(null, null)); // 获取所有拼团记录
summaryVO.setSuccessCount(combinationRecordService.getCombinationRecordCount( // 获取成团记录
CombinationRecordStatusEnum.SUCCESS.getStatus(), null));
summaryVO.setVirtualGroupCount(combinationRecordService.getCombinationRecordCount(null, Boolean.TRUE));// 获取虚拟成团记录
return success(summaryVO);
}
}

View File

@ -48,6 +48,10 @@ public class CombinationActivityBaseVO {
@NotNull(message = "开团人数不能为空")
private Integer userSize;
@Schema(description = "虚拟成团", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
@NotNull(message = "虚拟成团不能为空")
private Boolean virtualGroup;
@Schema(description = "限制时长(小时)", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@NotNull(message = "限制时长不能为空")
private Integer limitDuration;

View File

@ -0,0 +1,53 @@
package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductRespVO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - 拼团活动的分页项 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CombinationActivityPageItemRespVO extends CombinationActivityBaseVO {
@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, example = "0")
private Integer status;
@Schema(description = "拼团商品", requiredMode = Schema.RequiredMode.REQUIRED)
private List<CombinationProductRespVO> products;
// ========== 商品字段 ==========
@Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED, // SPU name 读取
example = "618大促")
private String spuName;
@Schema(description = "商品主图", requiredMode = Schema.RequiredMode.REQUIRED, // SPU picUrl 读取
example = "https://www.iocoder.cn/xx.png")
private String picUrl;
@Schema(description = "商品市场价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, // SPU marketPrice 读取
example = "50")
private Integer marketPrice;
// ========== 统计字段 ==========
@Schema(description = "开团组数", requiredMode = Schema.RequiredMode.REQUIRED, example = "33")
private Integer groupCount;
@Schema(description = "成团组数", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
private Integer groupSuccessCount;
@Schema(description = "购买次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
private Integer recordCount;
}

View File

@ -15,8 +15,7 @@ public class CombinationActivityPageReqVO extends PageParam {
@Schema(description = "拼团名称", example = "赵六")
private String name;
@Schema(description = "活动状态0开启 1关闭", example = "0")
@Schema(description = "活动状态", example = "0")
private Integer status;
}

View File

@ -6,8 +6,6 @@ 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;
@ -20,30 +18,12 @@ public class CombinationActivityRespVO extends CombinationActivityBaseVO {
@Schema(description = "活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "22901")
private Long id;
@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)
private LocalDateTime createTime;
@Schema(description = "开团人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "666")
private Integer userSize;
@Schema(description = "开团组数", requiredMode = Schema.RequiredMode.REQUIRED, example = "33")
private Integer totalCount;
@Schema(description = "成团组数", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
private Integer successCount;
@Schema(description = "虚拟成团", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
private Integer virtualGroup;
@Schema(description = "活动状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
private Integer status;
@Schema(description = "拼团商品", requiredMode = Schema.RequiredMode.REQUIRED)
private List<CombinationProductRespVO> products;

View File

@ -0,0 +1,82 @@
package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod;
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 CombinationRecordBaseVO {
@Schema(description = "拼团记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "拼团活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long activityId;
@Schema(description = "团长编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long headId;
// ========== 用户相关 ==========
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "9430")
@NotNull(message = "用户编号不能为空")
private Long userId;
@Schema(description = "用户昵称", example = "老芋艿")
private String nickname;
@Schema(description = "用户头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xxx.jpg")
private String avatar;
// ========== 商品相关 ==========
@Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23622")
@NotNull(message = "商品 SPU 编号不能为空")
private Long spuId;
@Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "29950")
@NotNull(message = "商品 SKU 编号不能为空")
private Long skuId;
@Schema(description = "商品名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是大黄豆")
private String spuName;
@Schema(description = "商品图片", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
private String picUrl;
@Schema(description = "过期时间", requiredMode = Schema.RequiredMode.REQUIRED)
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime expireTime;
@Schema(description = "可参团人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
private Integer userSize;
@Schema(description = "已参团人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
private Integer userCount;
@Schema(description = "拼团状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status;
@Schema(description = "是否虚拟成团", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
private Boolean virtualGroup;
@Schema(description = "开始时间 (订单付款后开始的时间)", requiredMode = Schema.RequiredMode.REQUIRED)
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime startTime;
@Schema(description = "结束时间(成团时间/失败时间)", requiredMode = Schema.RequiredMode.REQUIRED)
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime endTime;
}

View File

@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityRespVO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 拼团记录的分页项 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CombinationRecordPageItemRespVO extends CombinationRecordBaseVO {
// ========== 活动相关 ==========
private CombinationActivityRespVO activity;
}

View File

@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.promotion.enums.bargain.BargainRecordStatusEnum;
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 CombinationRecordReqPageVO extends PageParam {
@Schema(description = "活动状态", example = "1")
@InEnum(BargainRecordStatusEnum.class)
private Integer status;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 拼团记录信息统计 Response VO")
@Data
public class CombinationRecordSummaryVO {
@Schema(description = "所有拼团记录", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long userCount;
@Schema(description = "成团记录", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long successCount;
@Schema(description = "虚拟成团记录", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long virtualGroupCount;
}

View File

@ -18,16 +18,6 @@ public class SeckillActivityRespVO extends SeckillActivityBaseVO {
@Schema(description = "秒杀活动 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED, // SPU name 读取
example = "618大促")
private String spuName;
@Schema(description = "商品主图", requiredMode = Schema.RequiredMode.REQUIRED, // SPU picUrl 读取
example = "https://www.iocoder.cn/xx.png")
private String picUrl;
@Schema(description = "商品市场价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, // SPU marketPrice 读取
example = "50")
private Integer marketPrice;
@Schema(description = "秒杀商品", requiredMode = Schema.RequiredMode.REQUIRED)
private List<SeckillProductRespVO> products;
@ -52,4 +42,16 @@ public class SeckillActivityRespVO extends SeckillActivityBaseVO {
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
// ========== 商品字段 ==========
@Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED, // SPU name 读取
example = "618大促")
private String spuName;
@Schema(description = "商品主图", requiredMode = Schema.RequiredMode.REQUIRED, // SPU picUrl 读取
example = "https://www.iocoder.cn/xx.png")
private String picUrl;
@Schema(description = "商品市场价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, // SPU marketPrice 读取
example = "50")
private Integer marketPrice;
}

View File

@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.promotion.controller.app.activity;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.promotion.controller.app.activity.vo.AppActivityRespVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;
@ -21,12 +22,10 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;
@Tag(name = "用户 APP - 营销活动") // 用于提供跨多个活动的 HTTP 接口
@RestController
@ -42,58 +41,60 @@ public class AppActivityController {
private BargainActivityService bargainActivityService;
@GetMapping("/list-by-spu-id")
@Operation(summary = "获得单个商品,近期参与的每个活动") // 每种活动只返回一个
@Operation(summary = "获得单个商品,近期参与的每个活动")
@Parameter(name = "spuId", description = "商品编号", required = true)
public CommonResult<List<AppActivityRespVO>> getActivityListBySpuId(@RequestParam("spuId") Long spuId) {
return success(getAppActivityRespVOList(spuId));
// 每种活动只返回一个
return success(getAppActivityRespVOList(Collections.singletonList(spuId)));
}
@GetMapping("/list-by-spu-ids")
@Operation(summary = "获得多个商品,近期参与的每个活动") // 每种活动只返回一个key SPU 编号
@Operation(summary = "获得多个商品,近期参与的每个活动")
@Parameter(name = "spuIds", description = "商品编号数组", required = true)
public CommonResult<Map<Long, List<AppActivityRespVO>>> getActivityListBySpuIds(@RequestParam("spuIds") List<Long> spuIds) {
if (CollUtil.isEmpty(spuIds)) {
return success(MapUtil.empty());
}
Map<Long, List<AppActivityRespVO>> map = new HashMap<>(spuIds.size());
spuIds.forEach(spuId -> {
map.put(spuId, getAppActivityRespVOList(spuId));
});
return success(map);
// 每种活动只返回一个key SPU 编号
return success(convertMultiMap(getAppActivityRespVOList(spuIds), AppActivityRespVO::getSpuId));
}
private List<AppActivityRespVO> getAppActivityRespVOList(Long spuId) {
List<AppActivityRespVO> respList = new ArrayList<>();
CombinationActivityDO combination = combinationActivityService.getCombinationActivityBySpuId(spuId);
if (combination != null) {
respList.add(new AppActivityRespVO()
.setId(combination.getId())
.setType(PromotionTypeEnum.COMBINATION_ACTIVITY.getType())
.setName(combination.getName())
.setStartTime(combination.getStartTime())
.setEndTime(combination.getEndTime()));
private List<AppActivityRespVO> getAppActivityRespVOList(Collection<Long> spuIds) {
if (CollUtil.isEmpty(spuIds)) {
return new ArrayList<>();
}
SeckillActivityDO seckill = seckillActivityService.getSeckillActivityBySpuId(spuId);
if (seckill != null) {
respList.add(new AppActivityRespVO()
.setId(seckill.getId())
.setType(PromotionTypeEnum.SECKILL_ACTIVITY.getType())
.setName(seckill.getName())
.setStartTime(seckill.getStartTime())
.setEndTime(seckill.getEndTime()));
List<AppActivityRespVO> activityList = new ArrayList<>();
// 拼团活动
List<CombinationActivityDO> combinationActivities = combinationActivityService.getCombinationActivityBySpuIdsAndStatus(
spuIds, CommonStatusEnum.ENABLE.getStatus());
if (CollUtil.isNotEmpty(combinationActivities)) {
combinationActivities.forEach(item -> {
activityList.add(new AppActivityRespVO().setId(item.getId())
.setType(PromotionTypeEnum.COMBINATION_ACTIVITY.getType()).setName(item.getName())
.setSpuId(item.getSpuId()).setStartTime(item.getStartTime()).setEndTime(item.getEndTime()));
});
}
BargainActivityDO bargain = bargainActivityService.getBargainActivityBySpuId(spuId);
if (bargain != null) {
respList.add(new AppActivityRespVO()
.setId(bargain.getId())
.setType(PromotionTypeEnum.BARGAIN_ACTIVITY.getType())
.setName(bargain.getName())
.setStartTime(bargain.getStartTime())
.setEndTime(bargain.getEndTime()));
// 秒杀活动
List<SeckillActivityDO> seckillActivities = seckillActivityService.getSeckillActivityBySpuIdsAndStatus(
spuIds, CommonStatusEnum.ENABLE.getStatus());
if (CollUtil.isNotEmpty(seckillActivities)) {
seckillActivities.forEach(item -> {
activityList.add(new AppActivityRespVO().setId(item.getId())
.setType(PromotionTypeEnum.SECKILL_ACTIVITY.getType()).setName(item.getName())
.setSpuId(item.getSpuId()).setStartTime(item.getStartTime()).setEndTime(item.getEndTime()));
});
}
return respList;
// 砍价活动
List<BargainActivityDO> bargainActivities = bargainActivityService.getBargainActivityBySpuIdsAndStatus(
spuIds, CommonStatusEnum.ENABLE.getStatus());
if (CollUtil.isNotEmpty(bargainActivities)) {
bargainActivities.forEach(item -> {
activityList.add(new AppActivityRespVO().setId(item.getId())
.setType(PromotionTypeEnum.BARGAIN_ACTIVITY.getType()).setName(item.getName())
.setSpuId(item.getSpuId()).setStartTime(item.getStartTime()).setEndTime(item.getEndTime()));
});
}
return activityList;
}
}

View File

@ -13,12 +13,14 @@ public class AppActivityRespVO {
private Long id;
@Schema(description = "活动类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
// 对应 PromotionTypeEnum 枚举
private Integer type;
private Integer type; // 对应 PromotionTypeEnum 枚举
@Schema(description = "活动名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "618 大促")
private String name;
@Schema(description = "spu 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "618")
private Long spuId;
@Schema(description = "活动开始时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime startTime;

View File

@ -30,7 +30,6 @@ import java.time.Duration;
import java.util.Collections;
import java.util.List;
import static cn.hutool.core.util.ObjectUtil.defaultIfNull;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.cache.CacheUtils.buildAsyncReloadingCache;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
@ -56,6 +55,7 @@ public class AppCombinationActivityController {
@Resource
private CombinationActivityService activityService;
@Resource
private ProductSpuApi spuApi;
@ -68,40 +68,45 @@ public class AppCombinationActivityController {
}
private List<AppCombinationActivityRespVO> getCombinationActivityList0(Integer count) {
List<CombinationActivityDO> list = activityService.getCombinationActivityListByCount(defaultIfNull(count, 6));
if (CollUtil.isEmpty(list)) {
List<CombinationActivityDO> activityList = activityService.getCombinationActivityListByCount(count);
if (CollUtil.isEmpty(activityList)) {
return Collections.emptyList();
}
// 拼接返回
List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(list, CombinationActivityDO::getSpuId));
return CombinationActivityConvert.INSTANCE.convertAppList(list, spuList);
List<CombinationProductDO> productList = activityService.getCombinationProductListByActivityIds(
convertList(activityList, CombinationActivityDO::getId));
List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(activityList, CombinationActivityDO::getSpuId));
return CombinationActivityConvert.INSTANCE.convertAppList(activityList, productList, spuList);
}
@GetMapping("/page")
@Operation(summary = "获得拼团活动分页")
public CommonResult<PageResult<AppCombinationActivityRespVO>> getCombinationActivityPage(PageParam pageParam) {
PageResult<CombinationActivityDO> result = activityService.getCombinationActivityPage(pageParam);
if (CollUtil.isEmpty(result.getList())) {
return success(PageResult.empty(result.getTotal()));
PageResult<CombinationActivityDO> pageResult = activityService.getCombinationActivityPage(pageParam);
if (CollUtil.isEmpty(pageResult.getList())) {
return success(PageResult.empty(pageResult.getTotal()));
}
// 拼接返回
List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(result.getList(), CombinationActivityDO::getSpuId));
return success(CombinationActivityConvert.INSTANCE.convertAppPage(result, spuList));
List<CombinationProductDO> productList = activityService.getCombinationProductListByActivityIds(
convertList(pageResult.getList(), CombinationActivityDO::getId));
List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(pageResult.getList(), CombinationActivityDO::getSpuId));
return success(CombinationActivityConvert.INSTANCE.convertAppPage(pageResult, productList, spuList));
}
@GetMapping("/get-detail")
@Operation(summary = "获得拼团活动明细")
@Parameter(name = "id", description = "活动编号", required = true, example = "1024")
public CommonResult<AppCombinationActivityDetailRespVO> getCombinationActivityDetail(@RequestParam("id") Long id) {
// 1获取活动
CombinationActivityDO combinationActivity = activityService.getCombinationActivity(id);
if (combinationActivity == null
|| ObjectUtil.equal(combinationActivity.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {
// 1. 获取活动
CombinationActivityDO activity = activityService.getCombinationActivity(id);
if (activity == null
|| ObjectUtil.equal(activity.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {
return success(null);
}
// 2获取活动商品
List<CombinationProductDO> products = activityService.getCombinationProductsByActivityId(combinationActivity.getId());
return success(CombinationActivityConvert.INSTANCE.convert3(combinationActivity, products));
// 2. 获取活动商品
List<CombinationProductDO> products = activityService.getCombinationProductsByActivityId(activity.getId());
return success(CombinationActivityConvert.INSTANCE.convert3(activity, products));
}
}

View File

@ -1,18 +1,19 @@
package cn.iocoder.yudao.module.promotion.controller.app.combination;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record.AppCombinationRecordDetailRespVO;
import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record.AppCombinationRecordRespVO;
import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record.AppCombinationRecordSummaryRespVO;
import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
import cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordService;
import cn.iocoder.yudao.module.trade.api.order.TradeOrderApi;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.context.annotation.Lazy;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@ -23,6 +24,7 @@ import javax.annotation.Resource;
import javax.validation.constraints.Max;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
@ -36,24 +38,27 @@ public class AppCombinationRecordController {
@Resource
private CombinationRecordService combinationRecordService;
@Resource
@Lazy
private TradeOrderApi tradeOrderApi;
@GetMapping("/get-summary")
@Operation(summary = "获得拼团记录的概要信息", description = "用于小程序首页")
// TODO 芋艿增加 @Cache 缓存1 分钟过期
public CommonResult<AppCombinationRecordSummaryRespVO> getCombinationRecordSummary() {
// 获取所有拼团记录
Long count = combinationRecordService.getRecordsCount();
AppCombinationRecordSummaryRespVO summary = new AppCombinationRecordSummaryRespVO();
if (count == null || count == 0L) {
// 1. 获得拼团记录数量
Long count = combinationRecordService.getCombinationRecordCount(null, null);
if (count == 0) {
summary.setAvatars(Collections.emptyList());
summary.setUserCount(count);
return success(summary);
}
summary.setUserCount(count);
// TODO 只返回最近的 7
int num = 7;
summary.setAvatars(convertList(combinationRecordService.getLatestRecordList(num), CombinationRecordDO::getAvatar));
// 2. 获得拼团记录头像
List<CombinationRecordDO> records = combinationRecordService.getLatestCombinationRecordList(
AppCombinationRecordSummaryRespVO.AVATAR_COUNT);
summary.setAvatars(convertList(records, CombinationRecordDO::getAvatar));
return success(summary);
}
@ -61,7 +66,7 @@ public class AppCombinationRecordController {
@Operation(summary = "获得最近 n 条拼团记录(团长发起的)")
@Parameters({
@Parameter(name = "activityId", description = "拼团活动编号"),
@Parameter(name = "status", description = "状态"),
@Parameter(name = "status", description = "拼团状态"), // 对应 CombinationRecordStatusEnum 枚举
@Parameter(name = "count", description = "数量")
})
public CommonResult<List<AppCombinationRecordRespVO>> getHeadCombinationRecordList(
@ -69,44 +74,54 @@ public class AppCombinationRecordController {
@RequestParam("status") Integer status,
@RequestParam(value = "count", defaultValue = "20") @Max(20) Integer count) {
return success(CombinationActivityConvert.INSTANCE.convertList3(
combinationRecordService.getRecordListWithHead(activityId, status, count)));
combinationRecordService.getHeadCombinationRecordList(activityId, status, count)));
}
@GetMapping("/get-detail")
@Operation(summary = "获得拼团记录明细")
@Parameter(name = "id", description = "拼团记录编号", required = true, example = "1024")
public CommonResult<AppCombinationRecordDetailRespVO> getCombinationRecordDetail(@RequestParam("id") Long id) {
// 1查询这条记录
CombinationRecordDO record = combinationRecordService.getRecordById(id);
// 1. 查找这条拼团记录
CombinationRecordDO record = combinationRecordService.getCombinationRecordById(id);
if (record == null) {
return success(null);
}
AppCombinationRecordDetailRespVO detail = new AppCombinationRecordDetailRespVO();
List<CombinationRecordDO> records;
// 2判断是否为团长
if (record.getHeadId() == null) {
detail.setHeadRecord(CombinationActivityConvert.INSTANCE.convert(record));
// 2.1查找团员拼团记录
records = combinationRecordService.getRecordListByHeadId(record.getId());
} else {
// 2.2查找团长拼团记录
CombinationRecordDO headRecord = combinationRecordService.getRecordById(record.getHeadId());
if (headRecord == null) {
return success(null);
}
detail.setHeadRecord(CombinationActivityConvert.INSTANCE.convert(headRecord));
// 2.3查找团员拼团记录
records = combinationRecordService.getRecordListByHeadId(headRecord.getId());
// 2. 查找该拼团的参团记录
CombinationRecordDO headRecord;
List<CombinationRecordDO> memberRecords;
if (Objects.equals(record.getHeadId(), CombinationRecordDO.HEAD_ID_GROUP)) { // 情况一团长
headRecord = record;
memberRecords = combinationRecordService.getCombinationRecordListByHeadId(record.getId());
} else { // 情况二团员
headRecord = combinationRecordService.getCombinationRecordById(record.getHeadId());
memberRecords = combinationRecordService.getCombinationRecordListByHeadId(headRecord.getId());
}
detail.setMemberRecords(CombinationActivityConvert.INSTANCE.convertList3(records));
// 3获取当前用户参团记录订单编号
CombinationRecordDO userRecord = CollectionUtils.findFirst(records, r -> ObjectUtil.equal(r.getUserId(), getLoginUserId()));
detail.setOrderId(userRecord == null ? null : userRecord.getOrderId()); // 如果没参团返回 null
return success(detail);
// 3. 拼接数据
return success(CombinationActivityConvert.INSTANCE.convert(getLoginUserId(), headRecord, memberRecords));
}
@GetMapping("/cancel")
@Operation(summary = "取消拼团")
@Parameter(name = "id", description = "拼团记录编号", required = true, example = "1024")
public CommonResult<Boolean> cancelCombinationRecord(@RequestParam("id") Long id) {
Long userId = getLoginUserId();
// 1查找这条拼团记录
CombinationRecordDO record = combinationRecordService.getCombinationRecordByIdAndUser(userId, id);
if (record == null) {
return success(Boolean.FALSE);
}
// 1.1需要先校验拼团记录未完成
if (!CombinationRecordStatusEnum.isInProgress(record.getStatus())) {
return success(Boolean.FALSE);
}
// 2. 取消已支付的订单
tradeOrderApi.cancelPaidOrder(userId, record.getOrderId());
// 3. 取消拼团记录
combinationRecordService.cancelCombinationRecord(userId, record.getId(), record.getHeadId());
return success(Boolean.TRUE);
}
}

View File

@ -9,6 +9,11 @@ import java.util.List;
@Data
public class AppCombinationRecordSummaryRespVO {
/**
* 加载 {@link #avatars} 的数量
*/
public static final Integer AVATAR_COUNT = 7;
@Schema(description = "拼团用户数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long userCount;

View File

@ -86,7 +86,8 @@ public class AppSeckillActivityController {
// 2.1 查询满足当前阶段的活动
List<SeckillActivityDO> activityList = activityService.getSeckillActivityListByConfigIdAndStatus(config.getId(), CommonStatusEnum.ENABLE.getStatus());
List<SeckillProductDO> productList = activityService.getSeckillProductListByActivityId(convertList(activityList, SeckillActivityDO::getId));
List<SeckillProductDO> productList = activityService.getSeckillProductListByActivityId(
convertList(activityList, SeckillActivityDO::getId));
// 2.2 获取 spu 信息
List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(activityList, SeckillActivityDO::getSpuId));
return SeckillActivityConvert.INSTANCE.convert(config, activityList, productList, spuList);

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.promotion.convert.combination;
import cn.hutool.core.util.ObjectUtil;
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;
@ -8,25 +9,30 @@ import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityCreateReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityPageItemRespVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityRespVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityUpdateReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductBaseVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductRespVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordPageItemRespVO;
import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.activity.AppCombinationActivityDetailRespVO;
import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.activity.AppCombinationActivityRespVO;
import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record.AppCombinationRecordDetailRespVO;
import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record.AppCombinationRecordRespVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
/**
@ -53,20 +59,29 @@ public interface CombinationActivityConvert {
List<CombinationActivityRespVO> convertList(List<CombinationActivityDO> list);
PageResult<CombinationActivityRespVO> convertPage(PageResult<CombinationActivityDO> page);
default PageResult<CombinationActivityRespVO> convertPage(PageResult<CombinationActivityDO> page,
List<CombinationProductDO> productList,
List<ProductSpuRespDTO> spuList) {
default PageResult<CombinationActivityPageItemRespVO> convertPage(PageResult<CombinationActivityDO> page,
List<CombinationProductDO> productList,
Map<Long, Integer> groupCountMap,
Map<Long, Integer> groupSuccessCountMap,
Map<Long, Integer> recordCountMap,
List<ProductSpuRespDTO> spuList) {
PageResult<CombinationActivityPageItemRespVO> pageResult = convertPage(page);
Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);
PageResult<CombinationActivityRespVO> pageResult = convertPage(page);
pageResult.getList().forEach(item -> {
MapUtils.findAndThen(spuMap, item.getSpuId(), spu -> item.setSpuName(spu.getName()).setPicUrl(spu.getPicUrl()));
MapUtils.findAndThen(spuMap, item.getSpuId(), spu -> item.setSpuName(spu.getName()).setPicUrl(spu.getPicUrl())
.setMarketPrice(spu.getMarketPrice()));
item.setProducts(convertList2(productList));
// 设置统计字段
item.setGroupCount(groupCountMap.getOrDefault(item.getId(), 0))
.setGroupSuccessCount(groupSuccessCountMap.getOrDefault(item.getId(), 0))
.setRecordCount(recordCountMap.getOrDefault(item.getId(), 0));
});
return pageResult;
}
PageResult<CombinationActivityPageItemRespVO> convertPage(PageResult<CombinationActivityDO> page);
List<CombinationProductRespVO> convertList2(List<CombinationProductDO> productDOs);
@Mappings({
@ -97,23 +112,31 @@ public interface CombinationActivityConvert {
default CombinationRecordDO convert(CombinationRecordCreateReqDTO reqDTO,
CombinationActivityDO activity, MemberUserRespDTO user,
ProductSpuRespDTO spu, ProductSkuRespDTO sku) {
return convert(reqDTO)
.setCount(reqDTO.getCount())
.setVirtualGroup(false)
return convert(reqDTO).setVirtualGroup(false)
.setStatus(CombinationRecordStatusEnum.IN_PROGRESS.getStatus()) // 创建后默认状态为进行中
.setStartTime(LocalDateTime.now()) // TODO @puhui999想了下这个 startTime 应该是团长的
// TODO @puhui999有团长的情况下expireTime 应该是团长的
.setExpireTime(activity.getStartTime().plusHours(activity.getLimitDuration()))
.setUserSize(activity.getUserSize())
.setNickname(user.getNickname())
.setAvatar(user.getAvatar())
.setSpuName(spu.getName())
.setPicUrl(sku.getPicUrl());
.setUserSize(activity.getUserSize()).setUserCount(1) // 默认就是 1 插入后会接着更新一次所有的拼团记录
// 用户信息
.setNickname(user.getNickname()).setAvatar(user.getAvatar())
// 商品信息
.setSpuName(spu.getName()).setPicUrl(sku.getPicUrl());
}
List<AppCombinationActivityRespVO> convertAppList(List<CombinationActivityDO> list);
default List<AppCombinationActivityRespVO> convertAppList(List<CombinationActivityDO> list, List<ProductSpuRespDTO> spuList) {
default List<AppCombinationActivityRespVO> convertAppList(List<CombinationActivityDO> list,
List<CombinationProductDO> productList,
List<ProductSpuRespDTO> spuList) {
List<AppCombinationActivityRespVO> activityList = convertAppList(list);
Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);
Map<Long, List<CombinationProductDO>> productMap = convertMultiMap(productList, CombinationProductDO::getActivityId);
return CollectionUtils.convertList(activityList, item -> {
// 设置 product 信息
item.setCombinationPrice(getMinValue(productMap.get(item.getId()), CombinationProductDO::getCombinationPrice));
// 设置 SPU 信息
findAndThen(spuMap, item.getSpuId(), spu -> item.setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice()));
return item;
});
@ -121,13 +144,17 @@ public interface CombinationActivityConvert {
PageResult<AppCombinationActivityRespVO> convertAppPage(PageResult<CombinationActivityDO> result);
default PageResult<AppCombinationActivityRespVO> convertAppPage(PageResult<CombinationActivityDO> result, List<ProductSpuRespDTO> spuList) {
default PageResult<AppCombinationActivityRespVO> convertAppPage(PageResult<CombinationActivityDO> result,
List<CombinationProductDO> productList,
List<ProductSpuRespDTO> spuList) {
PageResult<AppCombinationActivityRespVO> appPage = convertAppPage(result);
Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);
Map<Long, List<CombinationProductDO>> productMap = convertMultiMap(productList, CombinationProductDO::getActivityId);
List<AppCombinationActivityRespVO> list = CollectionUtils.convertList(appPage.getList(), item -> {
findAndThen(spuMap, item.getSpuId(), spu -> {
item.setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice());
});
// 设置 product 信息
item.setCombinationPrice(getMinValue(productMap.get(item.getId()), CombinationProductDO::getCombinationPrice));
// 设置 SPU 信息
findAndThen(spuMap, item.getSpuId(), spu -> item.setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice()));
return item;
});
appPage.setList(list);
@ -146,4 +173,31 @@ public interface CombinationActivityConvert {
AppCombinationRecordRespVO convert(CombinationRecordDO record);
PageResult<CombinationRecordPageItemRespVO> convert(PageResult<CombinationRecordDO> result);
default PageResult<CombinationRecordPageItemRespVO> convert(PageResult<CombinationRecordDO> recordPage, List<CombinationActivityDO> activities) {
PageResult<CombinationRecordPageItemRespVO> result = convert(recordPage);
Map<Long, CombinationActivityDO> activityMap = convertMap(activities, CombinationActivityDO::getId);
result.setList(CollectionUtils.convertList(result.getList(), item -> {
findAndThen(activityMap, item.getActivityId(), activity -> {
item.setActivity(convert(activity));
});
return item;
}));
return result;
}
default AppCombinationRecordDetailRespVO convert(Long userId, CombinationRecordDO headRecord, List<CombinationRecordDO> memberRecords) {
AppCombinationRecordDetailRespVO respVO = new AppCombinationRecordDetailRespVO()
.setHeadRecord(convert(headRecord)).setMemberRecords(convertList3(memberRecords));
// 处理自己参与拼团的 orderId
CombinationRecordDO userRecord = CollectionUtils.findFirst(memberRecords, r -> ObjectUtil.equal(r.getUserId(), userId));
if (userRecord == null && ObjectUtil.equal(headRecord.getUserId(), userId)) {
userRecord = headRecord;
}
respVO.setOrderId(userRecord == null ? null : userRecord.getOrderId());
return respVO;
}
}

View File

@ -59,22 +59,10 @@ public class CombinationActivityDO extends BaseDO {
* 几人团
*/
private Integer userSize;
/**
* 开团组数
*/
private Integer totalCount;
/**
* 成团组数
*/
private Integer successCount;
/**
* 参与人数
*/
private Integer orderUserCount;
/**
* 虚拟成团
*/
private Integer virtualGroup;
private Boolean virtualGroup;
/**
* 活动状态
*

View File

@ -28,6 +28,11 @@ import java.time.LocalDateTime;
@AllArgsConstructor
public class CombinationRecordDO extends BaseDO {
/**
* 团长编号 - 团长
*/
public static final Long HEAD_ID_GROUP = 0L;
/**
* 编号主键自增
*/
@ -71,6 +76,7 @@ public class CombinationRecordDO extends BaseDO {
* 用户编号
*/
private Long userId;
/**
* 用户昵称
*/
@ -84,6 +90,8 @@ public class CombinationRecordDO extends BaseDO {
* 团长编号
*
* 关联 {@link CombinationRecordDO#getId()}
*
* 如果是团长则它的值是 {@link #HEAD_ID_GROUP}
*/
private Long headId;
/**
@ -108,6 +116,9 @@ public class CombinationRecordDO extends BaseDO {
private Integer userCount;
/**
* 是否虚拟成团
*
* 默认为 false
* 拼团过期都还没有成功如果 {@link CombinationActivityDO#getVirtualGroup()} true则执行虚拟成团的逻辑才会更新该字段为 true
*/
private Boolean virtualGroup;

View File

@ -8,8 +8,10 @@ import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.Ba
import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
/**
@ -83,11 +85,25 @@ public interface BargainActivityMapper extends BaseMapperX<BargainActivityDO> {
.last("LIMIT " + count));
}
default BargainActivityDO selectOne(Long spuId) {
return selectOne(new LambdaQueryWrapperX<BargainActivityDO>()
.eq(BargainActivityDO::getSpuId, spuId)
.orderByDesc(BargainActivityDO::getCreateTime)
, false);
}
// TODO @puhui999一个商品在统一时间不会参与多个活动so 是不是不用 inner join
// PS如果可以参与多个其实可以这样写 select * from promotion_bargain_activity group by spu_id ORDER BY create_time DESC通过 group 来过滤
/**
* 获取指定 spu 编号最近参加的活动每个 spuId 只返回一条记录
*
* @param spuIds spu 编号
* @param status 状态
* @return 砍价活动列表
*/
@Select("SELECT p1.* " +
"FROM promotion_bargain_activity p1 " +
"INNER JOIN ( " +
" SELECT spu_id, MAX(DISTINCT(create_time)) AS max_create_time " +
" FROM promotion_bargain_activity " +
" WHERE spu_id IN #{spuIds} " +
" GROUP BY spu_id " +
") p2 " +
"ON p1.spu_id = p2.spu_id AND p1.create_time = p2.max_create_time AND p1.status = #{status} " +
"ORDER BY p1.create_time DESC;")
List<BargainActivityDO> selectListBySpuIds(Collection<Long> spuIds, Integer status);
}

View File

@ -7,7 +7,10 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityPageReqVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.Collection;
import java.util.List;
/**
@ -40,11 +43,24 @@ public interface CombinationActivityMapper extends BaseMapperX<CombinationActivi
.last("LIMIT " + count));
}
default CombinationActivityDO selectOne(Long spuId) {
return selectOne(new LambdaQueryWrapperX<CombinationActivityDO>()
.eq(CombinationActivityDO::getSpuId, spuId)
.orderByDesc(CombinationActivityDO::getCreateTime)
, false);
}
// TODO @puhui999类似 BargainActivityMapper
/**
* 获取指定 spu 编号最近参加的活动每个 spuId 只返回一条记录
*
* @param spuIds spu 编号
* @param status 状态
* @return 拼团活动列表
*/
@Select("SELECT p1.* " +
"FROM promotion_combination_activity p1 " +
"INNER JOIN ( " +
" SELECT spu_id, MAX(DISTINCT(create_time)) AS max_create_time " +
" FROM promotion_combination_activity " +
" WHERE spu_id IN #{spuIds} " +
" GROUP BY spu_id " +
") p2 " +
"ON p1.spu_id = p2.spu_id AND p1.create_time = p2.max_create_time AND p1.status = #{status} " +
"ORDER BY p1.create_time DESC;")
List<CombinationActivityDO> selectListBySpuIds(@Param("spuIds") Collection<Long> spuIds, @Param("status") Integer status);
}

View File

@ -1,11 +1,20 @@
package cn.iocoder.yudao.module.promotion.dal.mysql.combination;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordReqPageVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* 拼团记录 Mapper
@ -20,34 +29,18 @@ public interface CombinationRecordMapper extends BaseMapperX<CombinationRecordDO
CombinationRecordDO::getOrderId, orderId);
}
default List<CombinationRecordDO> selectListByUserId(Long userId) {
return selectList(CombinationRecordDO::getUserId, userId);
}
default List<CombinationRecordDO> selectListByUserIdAndStatus(Long userId, Integer status) {
return selectList(new LambdaQueryWrapperX<CombinationRecordDO>()
.eq(CombinationRecordDO::getUserId, userId)
.eq(CombinationRecordDO::getStatus, status));
}
/**
* 查询拼团记录
*
* @param headId 团长编号
* @return 拼团记录
*/
default CombinationRecordDO selectOneByHeadId(Long headId, Integer status) {
default CombinationRecordDO selectByHeadId(Long headId, Integer status) {
return selectOne(new LambdaQueryWrapperX<CombinationRecordDO>()
.eq(CombinationRecordDO::getId, headId)
.eq(CombinationRecordDO::getStatus, status));
}
default List<CombinationRecordDO> selectListByHeadIdAndStatus(Long headId, Integer status) {
return selectList(new LambdaQueryWrapperX<CombinationRecordDO>()
.eq(CombinationRecordDO::getHeadId, headId)
.eq(CombinationRecordDO::getStatus, status));
}
/**
* 查询拼团记录
*
@ -69,25 +62,51 @@ public interface CombinationRecordMapper extends BaseMapperX<CombinationRecordDO
*/
default List<CombinationRecordDO> selectLatestList(int count) {
return selectList(new LambdaQueryWrapperX<CombinationRecordDO>()
.orderByDesc(CombinationRecordDO::getCreateTime)
.orderByDesc(CombinationRecordDO::getId)
.last("LIMIT " + count));
}
/**
* 获得最近 count 条拼团记录团长发起的
*
* @param activityId 拼团活动编号
* @param status 记录状态
* @param count 数量
* @return 拼团记录列表
*/
default List<CombinationRecordDO> selectList(Long activityId, Integer status, Integer count) {
default List<CombinationRecordDO> selectListByActivityIdAndStatusAndHeadId(Long activityId, Integer status,
Long headId, Integer count) {
return selectList(new LambdaQueryWrapperX<CombinationRecordDO>()
.eqIfPresent(CombinationRecordDO::getActivityId, activityId)
.eqIfPresent(CombinationRecordDO::getStatus, status)
.eq(CombinationRecordDO::getHeadId, null) // TODO 团长的 headId 是不是 null 还是自己的记录编号来着
.orderByDesc(CombinationRecordDO::getCreateTime)
.eq(CombinationRecordDO::getHeadId, headId)
.orderByDesc(CombinationRecordDO::getId)
.last("LIMIT " + count));
}
default Map<Long, Integer> selectCombinationRecordCountMapByActivityIdAndStatusAndHeadId(Collection<Long> activityIds,
Integer status, Long headId) {
// SQL count 查询
List<Map<String, Object>> result = selectMaps(new QueryWrapper<CombinationRecordDO>()
.select("COUNT(DISTINCT(user_id)) AS recordCount, activity_id AS activityId")
.in("activity_id", activityIds)
.eq(status != null, "status", status)
.eq(headId != null, "head_id", headId)
.groupBy("activity_id"));
if (CollUtil.isEmpty(result)) {
return Collections.emptyMap();
}
// 转换数据
return CollectionUtils.convertMap(result,
record -> MapUtil.getLong(record, "activityId"),
record -> MapUtil.getInt(record, "recordCount"));
}
default PageResult<CombinationRecordDO> selectPage(CombinationRecordReqPageVO pageVO) {
return selectPage(pageVO, new LambdaQueryWrapperX<CombinationRecordDO>()
.eqIfPresent(CombinationRecordDO::getStatus, pageVO.getStatus())
.betweenIfPresent(CombinationRecordDO::getCreateTime, pageVO.getCreateTime()));
}
// TODO @puhui999这个最好把 headId 也作为一个参数因为有个要求 userCount它要 DISTINCT 整体可以参考 selectCombinationRecordCountMapByActivityIdAndStatusAndHeadId
default Long selectCountByHeadAndStatusAndVirtualGroup(Integer status, Boolean virtualGroup) {
return selectCount(new LambdaQueryWrapperX<CombinationRecordDO>()
.eq(status != null || virtualGroup != null,
CombinationRecordDO::getHeadId, CombinationRecordDO.HEAD_ID_GROUP) // 统计团信息则指定团长
.eqIfPresent(CombinationRecordDO::getStatus, status)
.eqIfPresent(CombinationRecordDO::getVirtualGroup, virtualGroup));
}
}

View File

@ -9,7 +9,9 @@ import cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity.AppS
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillActivityDO;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.Collection;
import java.util.List;
/**
@ -56,11 +58,24 @@ public interface SeckillActivityMapper extends BaseMapperX<SeckillActivityDO> {
.apply(ObjectUtil.isNotNull(pageReqVO.getConfigId()), "FIND_IN_SET(" + pageReqVO.getConfigId() + ",config_ids) > 0"));
}
default SeckillActivityDO selectOne(Long spuId) {
return selectOne(new LambdaQueryWrapperX<SeckillActivityDO>()
.eq(SeckillActivityDO::getSpuId, spuId)
.orderByDesc(SeckillActivityDO::getCreateTime)
, false);
}
// TODO @puhui999类似 BargainActivityMapper
/**
* 获取指定 spu 编号最近参加的活动每个 spuId 只返回一条记录
*
* @param spuIds spu 编号
* @param status 状态
* @return 秒杀活动列表
*/
@Select("SELECT p1.* " +
"FROM promotion_seckill_activity p1 " +
"INNER JOIN ( " +
" SELECT spu_id, MAX(DISTINCT(create_time)) AS max_create_time " +
" FROM promotion_seckill_activity " +
" WHERE spu_id IN #{spuIds} " +
" GROUP BY spu_id " +
") p2 " +
"ON p1.spu_id = p2.spu_id AND p1.create_time = p2.max_create_time AND p1.status = #{status} " +
"ORDER BY p1.create_time DESC;")
List<SeckillActivityDO> selectListBySpuIds(Collection<Long> spuIds, Integer status);
}

View File

@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.Ba
import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;
import javax.validation.Valid;
import java.util.Collection;
import java.util.List;
import java.util.Set;
@ -99,11 +100,12 @@ public interface BargainActivityService {
List<BargainActivityDO> getBargainActivityListByCount(Integer count);
/**
* 获取指定 spu 编号的活动
* 获取指定 spu 编号最近参加的活动每个 spuId 只返回一条记录
*
* @param spuId spu 编号
* @return 砍价活动
* @param spuIds spu 编号
* @param status 状态
* @return 砍价活动列表
*/
BargainActivityDO getBargainActivityBySpuId(Long spuId);
List<BargainActivityDO> getBargainActivityBySpuIdsAndStatus(Collection<Long> spuIds, Integer status);
}

View File

@ -20,6 +20,7 @@ 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;
@ -176,8 +177,8 @@ public class BargainActivityServiceImpl implements BargainActivityService {
}
@Override
public BargainActivityDO getBargainActivityBySpuId(Long spuId) {
return bargainActivityMapper.selectOne(spuId);
public List<BargainActivityDO> getBargainActivityBySpuIdsAndStatus(Collection<Long> spuIds, Integer status) {
return bargainActivityMapper.selectListBySpuIds(spuIds, status);
}
}

View File

@ -73,7 +73,7 @@ public interface CombinationActivityService {
* @return 拼团活动的商品列表
*/
default List<CombinationProductDO> getCombinationProductsByActivityId(Long activityId) {
return getCombinationProductsByActivityIds(Collections.singletonList(activityId));
return getCombinationProductListByActivityIds(Collections.singletonList(activityId));
}
/**
@ -82,7 +82,15 @@ public interface CombinationActivityService {
* @param activityIds 拼团活动 ids
* @return 拼团活动的商品列表
*/
List<CombinationProductDO> getCombinationProductsByActivityIds(Collection<Long> activityIds);
List<CombinationProductDO> getCombinationProductListByActivityIds(Collection<Long> activityIds);
/**
* 获得拼团活动列表
*
* @param ids 拼团活动 ids
* @return 拼团活动的列表
*/
List<CombinationActivityDO> getCombinationActivityListByIds(Collection<Long> ids);
/**
* 获取正在进行的活动分页数据
@ -101,7 +109,7 @@ public interface CombinationActivityService {
PageResult<CombinationActivityDO> getCombinationActivityPage(PageParam pageParam);
/**
* 获取指定活动指定 sku 编号的商品
* 获取指定活动指定 sku 编号的商品
*
* @param activityId 活动编号
* @param skuId sku 编号
@ -110,11 +118,12 @@ public interface CombinationActivityService {
CombinationProductDO selectByActivityIdAndSkuId(Long activityId, Long skuId);
/**
* 获取指定 spu 编号的活动
* 获取指定 spu 编号最近参加的活动每个 spuId 只返回一条记录
*
* @param spuId spu 编号
* @return 拼团活动
* @param spuIds spu 编号
* @param status 状态
* @return 拼团活动列表
*/
CombinationActivityDO getCombinationActivityBySpuId(Long spuId);
List<CombinationActivityDO> getCombinationActivityBySpuIdsAndStatus(Collection<Long> spuIds, Integer status);
}

View File

@ -65,13 +65,11 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
// 插入拼团活动
CombinationActivityDO activity = CombinationActivityConvert.INSTANCE.convert(createReqVO)
.setStatus(CommonStatusEnum.ENABLE.getStatus())
.setTotalCount(0).setSuccessCount(0).setOrderUserCount(0).setVirtualGroup(0);
.setStatus(CommonStatusEnum.ENABLE.getStatus());
combinationActivityMapper.insert(activity);
// 插入商品
List<CombinationProductDO> products = CombinationActivityConvert.INSTANCE.convertList(createReqVO.getProducts(), activity);
combinationProductMapper.insertBatch(products);
// 返回
return activity.getId();
}
@ -202,10 +200,15 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
}
@Override
public List<CombinationProductDO> getCombinationProductsByActivityIds(Collection<Long> activityIds) {
public List<CombinationProductDO> getCombinationProductListByActivityIds(Collection<Long> activityIds) {
return combinationProductMapper.selectListByActivityIds(activityIds);
}
@Override
public List<CombinationActivityDO> getCombinationActivityListByIds(Collection<Long> ids) {
return combinationActivityMapper.selectList(CombinationActivityDO::getId, ids);
}
@Override
public List<CombinationActivityDO> getCombinationActivityListByCount(Integer count) {
return combinationActivityMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus(), count);
@ -224,8 +227,8 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
}
@Override
public CombinationActivityDO getCombinationActivityBySpuId(Long spuId) {
return combinationActivityMapper.selectOne(spuId);
public List<CombinationActivityDO> getCombinationActivityBySpuIdsAndStatus(Collection<Long> spuIds, Integer status) {
return combinationActivityMapper.selectListBySpuIds(spuIds, status);
}
}

View File

@ -1,14 +1,18 @@
package cn.iocoder.yudao.module.promotion.service.combination;
import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationValidateJoinRespDTO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordReqPageVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
import java.time.LocalDateTime;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* 拼团记录 Service 接口
@ -27,40 +31,34 @@ public interface CombinationRecordService {
void updateCombinationRecordStatusByUserIdAndOrderId(Integer status, Long userId, Long orderId);
/**
* 校验是否满足拼团条件
* 如果不满足会抛出异常
* 下单前校验是否满足拼团活动条件
*
* 如果校验失败则抛出业务异常
*
* @param activityId 活动编号
* @param userId 用户编号
* @param activityId 活动编号
* @param headId 团长编号
* @param skuId sku 编号
* @param count 数量
* @return 返回拼团活动和拼团活动商品
* @return 拼团信息
*/
KeyValue<CombinationActivityDO, CombinationProductDO> validateCombinationRecord(Long activityId, Long userId, Long skuId, Integer count);
KeyValue<CombinationActivityDO, CombinationProductDO> validateCombinationRecord(Long userId, Long activityId, Long headId,
Long skuId, Integer count);
/**
* 创建拼团记录
*
* @param reqDTO 创建信息
* @return 开团记录编号
*/
void createCombinationRecord(CombinationRecordCreateReqDTO reqDTO);
Long createCombinationRecord(CombinationRecordCreateReqDTO reqDTO);
/**
* 更新拼团状态和开始时间
*
* @param status 状态
* @param userId 用户编号
* @param orderId 订单编号
* @param startTime 开始时间
*/
void updateRecordStatusAndStartTimeByUserIdAndOrderId(Integer status, Long userId, Long orderId, LocalDateTime startTime);
/**
* 获得拼团状态
* 获得拼团记录
*
* @param userId 用户编号
* @param orderId 订单编号
* @return 拼团状态
* @return 拼团记录
*/
CombinationRecordDO getCombinationRecord(Long userId, Long orderId);
@ -71,27 +69,30 @@ public interface CombinationRecordService {
* @param activityId 活动 id
* @return 拼团记录列表
*/
List<CombinationRecordDO> getRecordListByUserIdAndActivityId(Long userId, Long activityId);
List<CombinationRecordDO> getCombinationRecordListByUserIdAndActivityId(Long userId, Long activityId);
/**
* 下单前校验是否满足拼团活动条件
*
* 如果校验失败则抛出业务异常
*
* @param activityId 活动编号
* @param userId 用户编号
* @param activityId 活动编号
* @param headId 团长编号
* @param skuId sku 编号
* @param count 数量
* @return 拼团信息
*/
CombinationValidateJoinRespDTO validateJoinCombination(Long activityId, Long userId, Long skuId, Integer count);
CombinationValidateJoinRespDTO validateJoinCombination(Long userId, Long activityId, Long headId, Long skuId, Integer count);
/**
* 获取所有拼团记录数
* 获取拼团记录数
*
* @param status 状态-允许为空
* @param virtualGroup 是否虚拟成团-允许为空
* @return 记录数
*/
Long getRecordsCount();
Long getCombinationRecordCount(@Nullable Integer status, @Nullable Boolean virtualGroup);
/**
* 获取最近的 count 条拼团记录
@ -99,7 +100,7 @@ public interface CombinationRecordService {
* @param count 限制数量
* @return 拼团记录列表
*/
List<CombinationRecordDO> getLatestRecordList(int count);
List<CombinationRecordDO> getLatestCombinationRecordList(int count);
/**
* 获得最近 n 条拼团记录团长发起的
@ -109,7 +110,7 @@ public interface CombinationRecordService {
* @param count 数量
* @return 拼团记录列表
*/
List<CombinationRecordDO> getRecordListWithHead(Long activityId, Integer status, Integer count);
List<CombinationRecordDO> getHeadCombinationRecordList(Long activityId, Integer status, Integer count);
/**
* 获取指定编号的拼团记录
@ -117,7 +118,7 @@ public interface CombinationRecordService {
* @param id 拼团记录编号
* @return 拼团记录
*/
CombinationRecordDO getRecordById(Long id);
CombinationRecordDO getCombinationRecordById(Long id);
/**
* 获取指定团长编号的拼团记录
@ -125,6 +126,46 @@ public interface CombinationRecordService {
* @param headId 团长编号
* @return 拼团记录列表
*/
List<CombinationRecordDO> getRecordListByHeadId(Long headId);
List<CombinationRecordDO> getCombinationRecordListByHeadId(Long headId);
/**
* 获取拼团记录分页数据
*
* @param pageVO 分页请求
* @return 拼团记录分页数据
*/
PageResult<CombinationRecordDO> getCombinationRecordPage(CombinationRecordReqPageVO pageVO);
/**
* 拼团活动获得拼团记录数量 Map
*
* @param activityIds 活动记录编号数组
* @param status 拼团状态允许空
* @param headId 团长编号允许空目的 headId 设置为 {@link CombinationRecordDO#HEAD_ID_GROUP} 可以设置
* @return 拼团记录数量 Map
*/
Map<Long, Integer> getCombinationRecordCountMapByActivity(Collection<Long> activityIds,
@Nullable Integer status,
@Nullable Long headId);
/**
* 获取拼团记录
*
* @param userId 用户编号
* @param id 拼团记录编号
* @return 拼团记录
*/
CombinationRecordDO getCombinationRecordByIdAndUser(Long userId, Long id);
/**
* 取消拼团
*
* @param userId 用户编号
* @param id 拼团记录编号
* @param headId 团长编号
*/
void cancelCombinationRecord(Long userId, Long id, Long headId);
}

View File

@ -1,9 +1,10 @@
package cn.iocoder.yudao.module.promotion.service.combination;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
@ -12,6 +13,7 @@ import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationValidateJoinRespDTO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordReqPageVO;
import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;
@ -19,18 +21,20 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationR
import cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationRecordMapper;
import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
import cn.iocoder.yudao.module.trade.api.order.TradeOrderApi;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Nullable;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.*;
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.framework.common.util.collection.CollectionUtils.findFirst;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getSumValue;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.afterNow;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.beforeNow;
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
// TODO 芋艿等拼团记录做完完整 review
@ -61,6 +65,7 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
@Resource
private TradeOrderApi tradeOrderApi;
// TODO @芋艿在详细预览下
@Override
@Transactional(rollbackFor = Exception.class)
public void updateCombinationRecordStatusByUserIdAndOrderId(Integer status, Long userId, Long orderId) {
@ -72,30 +77,6 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
recordMapper.updateById(record);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateRecordStatusAndStartTimeByUserIdAndOrderId(Integer status, Long userId, Long orderId, LocalDateTime startTime) {
CombinationRecordDO record = validateCombinationRecord(userId, orderId);
// 更新状态
record.setStatus(status);
// 更新开始时间
record.setStartTime(startTime);
recordMapper.updateById(record);
// 更新拼团参入人数
List<CombinationRecordDO> records = recordMapper.selectListByHeadIdAndStatus(record.getHeadId(), status);
if (CollUtil.isNotEmpty(records)) {
records.forEach(item -> {
item.setUserCount(records.size());
// 校验拼团是否满足要求
if (ObjectUtil.equal(records.size(), record.getUserSize())) {
item.setStatus(CombinationRecordStatusEnum.SUCCESS.getStatus());
}
});
}
recordMapper.updateBatch(records);
}
private CombinationRecordDO validateCombinationRecord(Long userId, Long orderId) {
// 校验拼团是否存在
CombinationRecordDO recordDO = recordMapper.selectByUserIdAndOrderId(userId, orderId);
@ -108,141 +89,247 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
// TODO @芋艿在详细预览下
@Override
public KeyValue<CombinationActivityDO, CombinationProductDO> validateCombinationRecord(
Long activityId, Long userId, Long skuId, Integer count) {
// 1.1 校验拼团活动是否存在
Long userId, Long activityId, Long headId, Long skuId, Integer count) {
// 1. 校验拼团活动是否存在
CombinationActivityDO activity = combinationActivityService.validateCombinationActivityExists(activityId);
// 1.2 校验活动是否开启
if (ObjectUtil.equal(activity.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {
// 1.1 校验活动是否开启
if (ObjUtil.equal(activity.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {
throw exception(COMBINATION_ACTIVITY_STATUS_DISABLE);
}
// 2 校验是否超出单次限购数量
// 1.2. 校验活动开始时间
if (afterNow(activity.getStartTime())) {
throw exception(COMBINATION_RECORD_FAILED_TIME_NOT_START);
}
// 1.3 校验是否超出单次限购数量
if (count > activity.getSingleLimitCount()) {
throw exception(COMBINATION_RECORD_FAILED_SINGLE_LIMIT_COUNT_EXCEED);
}
// 2.1校验活动商品是否存在
// 2. 父拼团是否存在,是否已经满了
if (headId != null) {
// 2.1. 查询进行中的父拼团
CombinationRecordDO record = recordMapper.selectByHeadId(headId, CombinationRecordStatusEnum.IN_PROGRESS.getStatus());
if (record == null) {
throw exception(COMBINATION_RECORD_HEAD_NOT_EXISTS);
}
// 2.2. 校验拼团是否已满
if (ObjUtil.equal(record.getUserCount(), record.getUserSize())) {
throw exception(COMBINATION_RECORD_USER_FULL);
}
// 2.3 校验拼团是否过期有父拼团的时候只校验父拼团的过期时间
if (beforeNow(record.getExpireTime())) {
throw exception(COMBINATION_RECORD_FAILED_TIME_END);
}
} else {
// 3. 校验当前活动是否结束(自己是父拼团的时候才校验活动是否结束)
if (beforeNow(activity.getEndTime())) {
throw exception(COMBINATION_RECORD_FAILED_TIME_END);
}
}
// 4.1 校验活动商品是否存在
CombinationProductDO product = combinationActivityService.selectByActivityIdAndSkuId(activityId, skuId);
if (product == null) {
throw exception(COMBINATION_JOIN_ACTIVITY_PRODUCT_NOT_EXISTS);
}
// 2.2校验 sku 是否存在
// 4.2 校验 sku 是否存在
ProductSkuRespDTO sku = productSkuApi.getSku(skuId);
if (sku == null) {
throw exception(COMBINATION_JOIN_ACTIVITY_PRODUCT_NOT_EXISTS);
}
// 2.3 校验库存是否充足
// 4.3 校验库存是否充足
if (count > sku.getStock()) {
throw exception(COMBINATION_ACTIVITY_UPDATE_STOCK_FAIL);
}
// 3校验是否有拼团记录
List<CombinationRecordDO> recordList = getRecordListByUserIdAndActivityId(userId, activityId);
if (CollUtil.isEmpty(recordList)) {
// 6.1 校验是否有拼团记录
List<CombinationRecordDO> recordList = recordMapper.selectListByUserIdAndActivityId(userId, activityId);
recordList.removeIf(record -> CombinationRecordStatusEnum.isFailed(record.getStatus())); // 取消的订单不算数
if (CollUtil.isEmpty(recordList)) { // 如果为空说明可以参与直接返回
return new KeyValue<>(activity, product);
}
// 4校验是否超出总限购数量
Integer sumValue = getSumValue(convertList(recordList, CombinationRecordDO::getCount,
item -> ObjectUtil.equals(item.getStatus(), CombinationRecordStatusEnum.SUCCESS.getStatus())), i -> i, Integer::sum);
if ((sumValue + count) > activity.getTotalLimitCount()) {
// 6.2 校验用户是否有该活动正在进行的拼团
CombinationRecordDO inProgressRecord = findFirst(recordList,
record -> CombinationRecordStatusEnum.isInProgress(record.getStatus()));
if (inProgressRecord != null) {
throw exception(COMBINATION_RECORD_FAILED_HAVE_JOINED);
}
// 6.3 校验是否超出总限购数量
Integer sumValue = getSumValue(recordList, CombinationRecordDO::getCount, Integer::sum);
if (sumValue != null && sumValue + count > activity.getTotalLimitCount()) {
throw exception(COMBINATION_RECORD_FAILED_TOTAL_LIMIT_COUNT_EXCEED);
}
// 5校验拼团记录是否存在未支付的订单如果存在未支付的订单则不允许发起新的拼团
CombinationRecordDO record = findFirst(recordList, item -> ObjectUtil.equals(item.getStatus(), null));
if (record == null) {
return new KeyValue<>(activity, product);
}
// 5.1查询关联的订单是否已经支付
// 当前 activityId 已经有未支付的订单不允许在发起新的要么支付要么去掉先
// TODO 芋艿看看是不是可以删除掉
Integer orderStatus = tradeOrderApi.getOrderStatus(record.getOrderId());
if (ObjectUtil.equal(orderStatus, TradeOrderStatusEnum.UNPAID.getStatus())) {
throw exception(COMBINATION_RECORD_FAILED_ORDER_STATUS_UNPAID);
}
return new KeyValue<>(activity, product);
}
// TODO 芋艿在详细 review
@Override
@Transactional(rollbackFor = Exception.class)
public void createCombinationRecord(CombinationRecordCreateReqDTO reqDTO) {
// 1校验拼团活动
KeyValue<CombinationActivityDO, CombinationProductDO> keyValue = validateCombinationRecord(
reqDTO.getActivityId(), reqDTO.getUserId(), reqDTO.getSkuId(), reqDTO.getCount());
CombinationActivityDO activity = keyValue.getKey();
// 2校验用户是否参加了其它拼团
List<CombinationRecordDO> recordDOList = recordMapper.selectListByUserIdAndStatus(reqDTO.getUserId(), CombinationRecordStatusEnum.IN_PROGRESS.getStatus());
if (CollUtil.isNotEmpty(recordDOList)) {
throw exception(COMBINATION_RECORD_FAILED_HAVE_JOINED);
}
// 3校验活动是否开启
if (LocalDateTime.now().isAfter(activity.getStartTime())) {
throw exception(COMBINATION_RECORD_FAILED_TIME_NOT_START);
}
// 4校验当前活动是否过期
if (LocalDateTime.now().isAfter(activity.getEndTime())) {
throw exception(COMBINATION_RECORD_FAILED_TIME_END);
}
// 5父拼团是否存在,是否已经满了
if (reqDTO.getHeadId() != null) {
// 5.1查询进行中的父拼团
CombinationRecordDO record = recordMapper.selectOneByHeadId(reqDTO.getHeadId(), CombinationRecordStatusEnum.IN_PROGRESS.getStatus());
if (record == null) {
throw exception(COMBINATION_RECORD_HEAD_NOT_EXISTS);
}
// 5.2校验拼团是否满足要求
if (ObjectUtil.equal(record.getUserCount(), record.getUserSize())) {
throw exception(COMBINATION_RECORD_USER_FULL);
}
}
public Long createCombinationRecord(CombinationRecordCreateReqDTO reqDTO) {
// 1. 校验拼团活动
KeyValue<CombinationActivityDO, CombinationProductDO> keyValue = validateCombinationRecord(reqDTO.getUserId(),
reqDTO.getActivityId(), reqDTO.getHeadId(), reqDTO.getSkuId(), reqDTO.getCount());
// 6. 创建拼团记录
// 2.1 组合数据创建拼团记录
MemberUserRespDTO user = memberUserApi.getUser(reqDTO.getUserId());
ProductSpuRespDTO spu = productSpuApi.getSpu(reqDTO.getSpuId());
ProductSkuRespDTO sku = productSkuApi.getSku(reqDTO.getSkuId());
recordMapper.insert(CombinationActivityConvert.INSTANCE.convert(reqDTO, activity, user, spu, sku));
CombinationRecordDO record = CombinationActivityConvert.INSTANCE.convert(reqDTO, keyValue.getKey(), user, spu, sku);
// 2.2 如果是团长需要设置 headId CombinationRecordDO#HEAD_ID_GROUP
if (record.getHeadId() == null) {
record.setHeadId(CombinationRecordDO.HEAD_ID_GROUP);
}
recordMapper.insert(record);
if (ObjUtil.equal(CombinationRecordDO.HEAD_ID_GROUP, record.getHeadId())) {
return record.getId();
}
// TODO @puhui是不是这里的更新放到 order 模块那支付完成后
// 4更新拼团相关信息到订单
tradeOrderApi.updateOrderCombinationInfo(record.getOrderId(), record.getActivityId(), record.getId(), record.getHeadId());
// 4更新拼团记录
updateCombinationRecordWhenCreate(reqDTO.getHeadId(), keyValue.getKey());
return record.getId();
}
/**
* 当新增拼团时更新拼团记录的进展
*
* @param headId 团长编号
* @param activity 活动
*/
private void updateCombinationRecordWhenCreate(Long headId, CombinationActivityDO activity) {
// 1. 团长 + 团员
List<CombinationRecordDO> records = getCombinationRecordListByHeadId(headId);
if (CollUtil.isEmpty(records)) {
return;
}
CombinationRecordDO headRecord = recordMapper.selectById(headId);
// 2. 批量更新记录
List<CombinationRecordDO> updateRecords = new ArrayList<>();
records.add(headRecord); // 加入团长团长也需要更新
boolean isFull = records.size() >= activity.getUserSize();
records.forEach(item -> {
CombinationRecordDO updateRecord = new CombinationRecordDO();
updateRecord.setId(item.getId()).setUserCount(records.size());
if (isFull) {
updateRecord.setStatus(CombinationRecordStatusEnum.SUCCESS.getStatus());
}
updateRecords.add(updateRecord);
});
recordMapper.updateBatch(updateRecords);
}
@Override
public CombinationRecordDO getCombinationRecord(Long userId, Long orderId) {
return validateCombinationRecord(userId, orderId);
return recordMapper.selectByUserIdAndOrderId(userId, orderId);
}
@Override
public List<CombinationRecordDO> getRecordListByUserIdAndActivityId(Long userId, Long activityId) {
public List<CombinationRecordDO> getCombinationRecordListByUserIdAndActivityId(Long userId, Long activityId) {
return recordMapper.selectListByUserIdAndActivityId(userId, activityId);
}
@Override
public CombinationValidateJoinRespDTO validateJoinCombination(Long activityId, Long userId, Long skuId, Integer count) {
KeyValue<CombinationActivityDO, CombinationProductDO> keyValue = validateCombinationRecord(activityId, userId, skuId, count);
return new CombinationValidateJoinRespDTO()
.setActivityId(keyValue.getKey().getId())
.setName(keyValue.getKey().getName())
.setCombinationPrice(keyValue.getValue().getCombinationPrice());
public CombinationValidateJoinRespDTO validateJoinCombination(Long userId, Long activityId, Long headId,
Long skuId, Integer count) {
KeyValue<CombinationActivityDO, CombinationProductDO> keyValue = validateCombinationRecord(userId, activityId,
headId, skuId, count);
return new CombinationValidateJoinRespDTO().setActivityId(keyValue.getKey().getId())
.setName(keyValue.getKey().getName()).setCombinationPrice(keyValue.getValue().getCombinationPrice());
}
@Override
public Long getRecordsCount() {
return recordMapper.selectCount();
public Long getCombinationRecordCount(@Nullable Integer status, @Nullable Boolean virtualGroup) {
return recordMapper.selectCountByHeadAndStatusAndVirtualGroup(status, virtualGroup);
}
@Override
public List<CombinationRecordDO> getLatestRecordList(int count) {
public List<CombinationRecordDO> getLatestCombinationRecordList(int count) {
return recordMapper.selectLatestList(count);
}
@Override
public List<CombinationRecordDO> getRecordListWithHead(Long activityId, Integer status, Integer count) {
return recordMapper.selectList(activityId, status, count);
public List<CombinationRecordDO> getHeadCombinationRecordList(Long activityId, Integer status, Integer count) {
return recordMapper.selectListByActivityIdAndStatusAndHeadId(activityId, status,
CombinationRecordDO.HEAD_ID_GROUP, count);
}
@Override
public CombinationRecordDO getRecordById(Long id) {
public CombinationRecordDO getCombinationRecordById(Long id) {
return recordMapper.selectById(id);
}
@Override
public List<CombinationRecordDO> getRecordListByHeadId(Long headId) {
public List<CombinationRecordDO> getCombinationRecordListByHeadId(Long headId) {
return recordMapper.selectList(CombinationRecordDO::getHeadId, headId);
}
@Override
public PageResult<CombinationRecordDO> getCombinationRecordPage(CombinationRecordReqPageVO pageVO) {
return recordMapper.selectPage(pageVO);
}
@Override
public Map<Long, Integer> getCombinationRecordCountMapByActivity(Collection<Long> activityIds,
@Nullable Integer status, @Nullable Long headId) {
return recordMapper.selectCombinationRecordCountMapByActivityIdAndStatusAndHeadId(activityIds, status, headId);
}
@Override
public CombinationRecordDO getCombinationRecordByIdAndUser(Long userId, Long id) {
return recordMapper.selectOne(CombinationRecordDO::getUserId, userId, CombinationRecordDO::getId, id);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void cancelCombinationRecord(Long userId, Long id, Long headId) {
// 删除记录
recordMapper.deleteById(id);
// 需要更新的记录
List<CombinationRecordDO> updateRecords = new ArrayList<>();
// 如果它是团长则顺序下单时间继承
if (Objects.equals(headId, CombinationRecordDO.HEAD_ID_GROUP)) { // 情况一团长
// 团员
List<CombinationRecordDO> list = getCombinationRecordListByHeadId(id);
if (CollUtil.isEmpty(list)) {
return;
}
// 按照创建时间升序排序
list.sort(Comparator.comparing(CombinationRecordDO::getCreateTime)); // 影响原 list
CombinationRecordDO newHead = list.get(0); // 新团长继位
list.forEach(item -> {
CombinationRecordDO recordDO = new CombinationRecordDO();
recordDO.setId(item.getId());
if (ObjUtil.equal(item.getId(), newHead.getId())) { // 新团长
recordDO.setHeadId(CombinationRecordDO.HEAD_ID_GROUP);
} else {
recordDO.setHeadId(newHead.getId());
}
recordDO.setUserCount(list.size());
updateRecords.add(recordDO);
});
} else { // 情况二团员
// 团长
CombinationRecordDO recordHead = recordMapper.selectById(headId);
// 团员
List<CombinationRecordDO> records = getCombinationRecordListByHeadId(headId);
if (CollUtil.isEmpty(records)) {
return;
}
records.add(recordHead); // 加入团长团长数据也需要更新
records.forEach(item -> {
CombinationRecordDO recordDO = new CombinationRecordDO();
recordDO.setId(item.getId());
recordDO.setUserCount(records.size());
updateRecords.add(recordDO);
});
}
// 更新拼团记录
recordMapper.updateBatch(updateRecords);
}
}

View File

@ -120,11 +120,12 @@ public interface SeckillActivityService {
SeckillValidateJoinRespDTO validateJoinSeckill(Long activityId, Long skuId, Integer count);
/**
* 获取指定 spu 编号的活动
* 获取指定 spu 编号最近参加的活动每个 spuId 只返回一条记录
*
* @param spuId spu 编号
* @return 秒杀活动
* @param spuIds spu 编号
* @param status 状态
* @return 秒杀活动列表
*/
SeckillActivityDO getSeckillActivityBySpuId(Long spuId);
List<SeckillActivityDO> getSeckillActivityBySpuIdsAndStatus(Collection<Long> spuIds, Integer status);
}

View File

@ -311,8 +311,8 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
}
@Override
public SeckillActivityDO getSeckillActivityBySpuId(Long spuId) {
return seckillActivityMapper.selectOne(spuId);
public List<SeckillActivityDO> getSeckillActivityBySpuIdsAndStatus(Collection<Long> spuIds, Integer status) {
return seckillActivityMapper.selectListBySpuIds(spuIds, status);
}
}

View File

@ -111,9 +111,6 @@ public class CombinationActivityServiceImplTest extends BaseDbUnitTest {
o.setStartTime(null);
o.setEndTime(null);
o.setUserSize(null);
o.setTotalCount(null);
o.setSuccessCount(null);
o.setOrderUserCount(null);
o.setVirtualGroup(null);
o.setStatus(null);
o.setLimitDuration(null);
@ -134,12 +131,6 @@ public class CombinationActivityServiceImplTest extends BaseDbUnitTest {
combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setEndTime(null)));
// 测试 userSize 不匹配
combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setUserSize(null)));
// 测试 totalNum 不匹配
combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setTotalCount(null)));
// 测试 successNum 不匹配
combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setSuccessCount(null)));
// 测试 orderUserCount 不匹配
combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setOrderUserCount(null)));
// 测试 virtualGroup 不匹配
combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setVirtualGroup(null)));
// 测试 status 不匹配
@ -173,9 +164,6 @@ public class CombinationActivityServiceImplTest extends BaseDbUnitTest {
o.setStartTime(null);
o.setEndTime(null);
o.setUserSize(null);
o.setTotalCount(null);
o.setSuccessCount(null);
o.setOrderUserCount(null);
o.setVirtualGroup(null);
o.setStatus(null);
o.setLimitDuration(null);
@ -196,12 +184,6 @@ public class CombinationActivityServiceImplTest extends BaseDbUnitTest {
combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setEndTime(null)));
// 测试 userSize 不匹配
combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setUserSize(null)));
// 测试 totalNum 不匹配
combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setTotalCount(null)));
// 测试 successNum 不匹配
combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setSuccessCount(null)));
// 测试 orderUserCount 不匹配
combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setOrderUserCount(null)));
// 测试 virtualGroup 不匹配
combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setVirtualGroup(null)));
// 测试 status 不匹配

View File

@ -28,13 +28,22 @@ public interface TradeOrderApi {
*/
TradeOrderRespDTO getOrder(Long id);
// TODO 芋艿看看是不是可以删除掉
/**
* 获取订单状态
* 更新拼团相关信息到订单
*
* @param id 订单编号
* @return 订单状态
* @param orderId 订单编号
* @param activityId 拼团活动编号
* @param combinationRecordId 拼团记录编号
* @param headId 团长编号
*/
Integer getOrderStatus(Long id);
void updateOrderCombinationInfo(Long orderId, Long activityId, Long combinationRecordId, Long headId);
/**
* 取消支付订单
*
* @param userId 用户编号
* @param orderId 订单编号
*/
void cancelPaidOrder(Long userId, Long orderId);
}

View File

@ -33,6 +33,7 @@ public interface ErrorCodeConstants {
ErrorCode ORDER_UPDATE_PRICE_FAIL_PRICE_ERROR = new ErrorCode(1_011_000_028, "支付订单调价失败,原因:调整后支付价格不能小于 0.01 元");
ErrorCode ORDER_DELETE_FAIL_STATUS_NOT_CANCEL = new ErrorCode(1_011_000_029, "交易订单删除失败,订单不是【已取消】状态");
ErrorCode ORDER_RECEIVE_FAIL_DELIVERY_TYPE_NOT_PICK_UP = new ErrorCode(1_011_000_030, "交易订单自提失败,收货方式不是【用户自提】");
ErrorCode ORDER_UPDATE_ADDRESS_FAIL_STATUS_NOT_DELIVERED = new ErrorCode(1_011_000_031, "交易订单修改收货地址失败,原因:订单已发货");
// ========== After Sale 模块 1-011-000-100 ==========
ErrorCode AFTER_SALE_NOT_FOUND = new ErrorCode(1_011_000_100, "售后单不存在");

View File

@ -16,7 +16,7 @@ public enum TradeOrderOperateTypeEnum {
MEMBER_CREATE(1, "用户下单"),
ADMIN_UPDATE_PRICE(2, "订单价格 {oldPayPrice} 修改,实际支付金额为 {newPayPrice} 元"),
MEMBER_PAY(10, "用户付款成功"),
SYSTEM_UP_ADDRESS(11, "订单 {orderId} 收货地址修改"),
ADMIN_UPDATE_ADDRESS(11, "收货地址修改"),
ADMIN_DELIVERY(20, "已发货,快递公司:{deliveryName},快递单号:{logisticsNo}"),
MEMBER_RECEIVE(30, "用户已收货"),
SYSTEM_RECEIVE(31, "到期未收货,系统自动确认收货"),

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.trade.enums.order;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@ -37,4 +38,20 @@ public enum TradeOrderTypeEnum implements IntArrayValuable {
return ARRAYS;
}
public static boolean isNormal(Integer type) {
return ObjectUtil.equal(type, NORMAL.getType());
}
public static boolean isSeckill(Integer type) {
return ObjectUtil.equal(type, SECKILL.getType());
}
public static boolean isBargain(Integer type) {
return ObjectUtil.equal(type, BARGAIN.getType());
}
public static boolean isCombination(Integer type) {
return ObjectUtil.equal(type, COMBINATION.getType());
}
}

View File

@ -4,6 +4,7 @@ import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderRespDTO;
import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService;
import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
@ -25,6 +26,8 @@ public class TradeOrderApiImpl implements TradeOrderApi {
@Resource
private TradeOrderQueryService tradeOrderQueryService;
@Resource
private TradeOrderUpdateService tradeOrderUpdateService;
@Override
public List<TradeOrderRespDTO> getOrderList(Collection<Long> ids) {
@ -36,6 +39,16 @@ public class TradeOrderApiImpl implements TradeOrderApi {
return TradeOrderConvert.INSTANCE.convert(tradeOrderQueryService.getOrder(id));
}
@Override
public void updateOrderCombinationInfo(Long orderId, Long activityId, Long combinationRecordId, Long headId) {
tradeOrderUpdateService.updateOrderCombinationInfo(orderId, activityId, combinationRecordId, headId);
}
@Override
public void cancelPaidOrder(Long userId, Long orderId) {
tradeOrderUpdateService.cancelPaidOrder(userId, orderId);
}
@Override
public Integer getOrderStatus(Long id) {
TradeOrderDO order = tradeOrderQueryService.getOrder(id);

View File

@ -52,7 +52,6 @@ public class AppTradeOrderSettlementReqVO {
private Long seckillActivityId;
// ========== 拼团活动相关字段 ==========
// TODO @puhui999是不是拼团记录的编号哈
@Schema(description = "拼团活动编号", example = "1024")
private Long combinationActivityId;

View File

@ -207,12 +207,16 @@ public interface TradeOrderConvert {
default TradePriceCalculateReqBO convert(Long userId, AppTradeOrderSettlementReqVO settlementReqVO,
List<CartDO> cartList) {
TradePriceCalculateReqBO reqBO = new TradePriceCalculateReqBO().setUserId(userId)
.setItems(new ArrayList<>(settlementReqVO.getItems().size()))
.setCouponId(settlementReqVO.getCouponId()).setPointStatus(settlementReqVO.getPointStatus())
// 物流信息
.setDeliveryType(settlementReqVO.getDeliveryType()).setAddressId(settlementReqVO.getAddressId())
.setPickUpStoreId(settlementReqVO.getPickUpStoreId())
.setItems(new ArrayList<>(settlementReqVO.getItems().size()))
// 各种活动
.setSeckillActivityId(settlementReqVO.getSeckillActivityId())
.setBargainRecordId(settlementReqVO.getBargainRecordId());
.setBargainRecordId(settlementReqVO.getBargainRecordId())
.setCombinationActivityId(settlementReqVO.getCombinationActivityId())
.setCombinationHeadId(settlementReqVO.getCombinationHeadId());
// 商品项的构建
Map<Long, CartDO> cartMap = convertMap(cartList, CartDO::getId);
for (AppTradeOrderSettlementReqVO.Item item : settlementReqVO.getItems()) {

View File

@ -318,10 +318,16 @@ public class TradeOrderDO extends BaseDO {
*/
private Long combinationActivityId;
/**
* 团长编号
* 拼团团长编号
*
* 关联 CombinationRecordDO headId 字段
*/
private Long combinationHeadId;
/**
* 拼团记录编号
*
* 关联 CombinationRecordDO id 字段
*/
private Long combinationHeadId;
private Long combinationRecordId;
}

View File

@ -15,4 +15,12 @@ public interface RedisKeyConstants {
*/
String TRADE_NO = "trade_no:";
/**
* 交易序号的缓存
*
* KEY 格式express_track:{code-logisticsNo-receiverMobile}
* VALUE 数据格式 String, 物流信息集合
*/
String EXPRESS_TRACK = "express_track";
}

View File

@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.trade.service.order;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
@ -13,11 +14,13 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper;
import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper;
import cn.iocoder.yudao.module.trade.dal.redis.RedisKeyConstants;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.ExpressClientFactory;
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO;
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO;
import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@ -141,7 +144,6 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService {
return tradeOrderItemMapper.selectProductSumByOrderId(convertSet(orders, TradeOrderDO::getId));
}
// TODO @puhui999可以加个 spring 缓存30 分钟主要考虑及时性要求不高但是每次调用需要钱
/**
* 获得订单的物流轨迹
*
@ -149,19 +151,34 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService {
* @return 物流轨迹
*/
private List<ExpressTrackRespDTO> getExpressTrackList(TradeOrderDO order) {
// 查询物流公司
if (order.getLogisticsId() == null) {
return Collections.emptyList();
}
// 查询物流公司
DeliveryExpressDO express = deliveryExpressService.getDeliveryExpress(order.getLogisticsId());
if (express == null) {
throw exception(EXPRESS_NOT_EXISTS);
}
// 查询物流轨迹
return getSelf().getExpressTrackList(express.getCode(), order.getLogisticsNo(), order.getReceiverMobile());
}
/**
* 查询物流轨迹
* 加个 spring 缓存30 分钟主要考虑及时性要求不高但是每次调用需要钱TODO @艿艿这个时间不会搞了交给你了哈哈哈
*
* @param code 快递公司编码
* @param logisticsNo 发货快递单号
* @param receiverMobile 寄件人的电话号码
* @return 物流轨迹
*/
@Cacheable(cacheNames = RedisKeyConstants.EXPRESS_TRACK, key = "#code + '-' + #logisticsNo + '-' + #receiverMobile",
condition = "#result != null")
public List<ExpressTrackRespDTO> getExpressTrackList(String code, String logisticsNo, String receiverMobile) {
// 查询物流轨迹
return expressClientFactory.getDefaultExpressClient().getExpressTrackList(
new ExpressTrackQueryReqDTO().setExpressCode(express.getCode()).setLogisticsNo(order.getLogisticsNo())
.setPhone(order.getReceiverMobile()));
new ExpressTrackQueryReqDTO().setExpressCode(code).setLogisticsNo(logisticsNo)
.setPhone(receiverMobile));
}
@ -190,4 +207,13 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService {
return tradeOrderItemMapper.selectListByOrderId(orderIds);
}
/**
* 获得自身的代理对象解决 AOP 生效问题
*
* @return 自己
*/
private TradeOrderQueryServiceImpl getSelf() {
return SpringUtil.getBean(getClass());
}
}

View File

@ -170,4 +170,23 @@ public interface TradeOrderUpdateService {
*/
int createOrderItemCommentBySystem();
/**
* 更新拼团相关信息到订单
*
* @param orderId 订单编号
* @param activityId 拼团活动编号
* @param combinationRecordId 拼团记录编号
* @param headId 团长编号
*/
// TODO 芋艿 review 拼团
void updateOrderCombinationInfo(Long orderId, Long activityId, Long combinationRecordId, Long headId);
/**
* 取消支付订单
*
* @param userId 用户编号
* @param orderId 订单编号
*/
void cancelPaidOrder(Long userId, Long orderId);
}

View File

@ -250,7 +250,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
/**
* 订单创建前执行前置逻辑
*
* @param order 订单
* @param order 订单
* @param orderItems 订单项
*/
private void beforeCreateTradeOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
@ -267,9 +267,9 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
* <p>
* 例如说优惠劵的扣减积分的扣减支付单的创建等等
*
* @param order 订单
* @param orderItems 订单项
* @param createReqVO 创建订单请求
* @param order 订单
* @param orderItems 订单项
* @param createReqVO 创建订单请求
*/
private void afterCreateTradeOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems,
AppTradeOrderCreateReqVO createReqVO) {
@ -331,7 +331,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
}
// 3订单支付成功后
tradeOrderHandlers.forEach(handler -> handler.afterPayOrder(order));
List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(id);
tradeOrderHandlers.forEach(handler -> handler.afterPayOrder(order, orderItems));
// 4.1 增加用户积分赠送
addUserPoint(order.getUserId(), order.getGivePoint(), MemberPointBizTypeEnum.ORDER_GIVE, order.getId());
@ -624,12 +625,11 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID);
}
// 2. TODO 活动相关库存回滚需要活动 id活动 id 怎么获取app 端能否传过来回复从订单里拿呀
tradeOrderHandlers.forEach(handler -> handler.cancelOrder());
// 3. 回滚库存
List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(id);
// 3. 回滚库存
productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convert(orderItems));
// 3.1 活动相关的回滚
tradeOrderHandlers.forEach(handler -> handler.cancelOrder(order, orderItems));
// 4. 回滚优惠券
if (order.getCouponId() != null && order.getCouponId() > 0) {
@ -738,19 +738,21 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
}
@Override
@TradeOrderLog(operateType = TradeOrderOperateTypeEnum.SYSTEM_UP_ADDRESS)
@TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ADMIN_UPDATE_ADDRESS)
public void updateOrderAddress(TradeOrderUpdateAddressReqVO reqVO) {
// 校验交易订单
TradeOrderDO order = validateOrderExists(reqVO.getId());
// TODO @puhui999是否需要校验订单是否发货
// TODO 发货后是否支持修改收货地址
// 发货后不允许修改
// TODO @puhui999只有待发货可以执行 update
if (TradeOrderStatusEnum.isDelivered(order.getStatus())) {
throw exception(ORDER_UPDATE_ADDRESS_FAIL_STATUS_NOT_DELIVERED);
}
// 更新
tradeOrderMapper.updateById(TradeOrderConvert.INSTANCE.convert(reqVO));
// 记录订单日志
TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), order.getStatus(),
MapUtil.<String, Object>builder().put("orderId", order.getId()).build());
TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), order.getStatus());
}
@Override
@ -806,10 +808,11 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
// 2.2 如果全部退款则进行取消订单
getSelf().cancelOrderByAfterSale(order, orderRefundPrice);
// TODO @puhui999活动相关的回滚
// 3. 回滚库存
productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convert(Collections.singletonList(orderItem)));
// 3.1 活动相关的回滚
tradeOrderHandlers.forEach(handler -> handler.cancelOrder(order, Collections.singletonList(orderItem)));
// 4.1 回滚积分扣减用户积分赠送的
reduceUserPoint(order.getUserId(), orderItem.getGivePoint(), MemberPointBizTypeEnum.AFTER_SALE_DEDUCT_GIVE, orderItem.getAfterSaleId());
@ -909,6 +912,25 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
return count;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateOrderCombinationInfo(Long orderId, Long activityId, Long combinationRecordId, Long headId) {
tradeOrderMapper.updateById(
new TradeOrderDO().setId(orderId).setCombinationActivityId(activityId)
.setCombinationRecordId(combinationRecordId).setCombinationHeadId(headId));
}
@Override
@Transactional(rollbackFor = Exception.class)
public void cancelPaidOrder(Long userId, Long orderId) {
TradeOrderDO order = tradeOrderMapper.selectOrderByIdAndUserId(orderId, userId);
if (order == null) {
throw exception(ORDER_NOT_FOUND);
}
cancelOrder0(order, TradeOrderCancelTypeEnum.MEMBER_CANCEL);
}
/**
* 创建单个订单的评论
*

View File

@ -1,6 +1,5 @@
package cn.iocoder.yudao.module.trade.service.order.handler;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.module.promotion.api.bargain.BargainActivityApi;
import cn.iocoder.yudao.module.promotion.api.bargain.BargainRecordApi;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
@ -26,9 +25,10 @@ public class TradeBargainHandler implements TradeOrderHandler {
@Override
public void beforeOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
if (ObjectUtil.notEqual(TradeOrderTypeEnum.BARGAIN.getType(), order.getType())) {
if (TradeOrderTypeEnum.isBargain(order.getType())) {
return;
}
// 扣减砍价活动的库存
bargainActivityApi.updateBargainActivityStock(order.getBargainActivityId(),
-orderItems.get(0).getCount());
@ -36,13 +36,20 @@ public class TradeBargainHandler implements TradeOrderHandler {
@Override
public void afterOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
if (ObjectUtil.notEqual(TradeOrderTypeEnum.BARGAIN.getType(), order.getType())) {
if (TradeOrderTypeEnum.isBargain(order.getType())) {
return;
}
// 记录砍价记录对应的订单编号
bargainRecordApi.updateBargainRecordOrderId(order.getBargainRecordId(), order.getId());
}
// TODO 芋艿取消订单时需要增加库存
@Override
public void cancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
if (TradeOrderTypeEnum.isBargain(order.getType())) {
return;
}
// TODO 芋艿取消订单时需要增加库存
}
}

View File

@ -1,7 +1,6 @@
package cn.iocoder.yudao.module.trade.service.order.handler;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.module.promotion.api.combination.CombinationRecordApi;
import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
@ -26,40 +25,37 @@ public class TradeCombinationHandler implements TradeOrderHandler {
@Override
public void beforeOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
// 如果不是拼团订单则结束
if (ObjectUtil.notEqual(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) {
if (TradeOrderTypeEnum.isCombination(order.getType())) {
return;
}
Assert.isTrue(orderItems.size() == 1, "拼团时,只允许选择一个商品");
// 获取商品信息
TradeOrderItemDO item = orderItems.get(0);
// 校验是否满足拼团活动相关限制
combinationRecordApi.validateCombinationRecord(order.getCombinationActivityId(), order.getUserId(), item.getSkuId(), item.getCount());
TradeOrderItemDO item = orderItems.get(0);
combinationRecordApi.validateCombinationRecord(order.getUserId(), order.getCombinationActivityId(),
order.getCombinationHeadId(), item.getSkuId(), item.getCount());
// TODO @puhui999这里还要限制下是不是已经 createOrder就是还没支付的时候重复下单了需要校验下不然的话一个拼团可以下多个单子了
}
@Override
public void afterOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
public void afterPayOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
// 如果不是拼团订单则结束
if (ObjectUtil.notEqual(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) {
if (TradeOrderTypeEnum.isCombination(order.getType())) {
return;
}
Assert.isTrue(orderItems.size() == 1, "拼团时,只允许选择一个商品");
// 获取商品信息
TradeOrderItemDO item = orderItems.get(0);
// 创建砍价记录
// 创建拼团记录
combinationRecordApi.createCombinationRecord(TradeOrderConvert.INSTANCE.convert(order, item));
}
@Override
public void afterPayOrder(TradeOrderDO order) {
// 如果不是拼团订单则结束
if (ObjectUtil.notEqual(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) {
public void cancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
if (TradeOrderTypeEnum.isCombination(order.getType())) {
return;
}
// 更新拼团状态 TODO puhui999订单支付失败或订单支付过期删除这条拼团记录
combinationRecordApi.updateRecordStatusToInProgress(order.getUserId(), order.getId(), order.getPayTime());
}
}

View File

@ -33,12 +33,18 @@ public interface TradeOrderHandler {
* 支付订单后
*
* @param order 订单
* @param orderItems 订单项
*/
default void afterPayOrder(TradeOrderDO order) {}
default void afterPayOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
}
/**
* 订单取消
*
* @param order 订单
* @param orderItems 订单项
*/
default void cancelOrder() {}
default void cancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
}
}

View File

@ -1,6 +1,5 @@
package cn.iocoder.yudao.module.trade.service.order.handler;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.module.promotion.api.seckill.SeckillActivityApi;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
@ -23,12 +22,21 @@ public class TradeSeckillHandler implements TradeOrderHandler {
@Override
public void beforeOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
if (ObjectUtil.notEqual(TradeOrderTypeEnum.SECKILL.getType(), order.getType())) {
if (TradeOrderTypeEnum.isSeckill(order.getType())) {
return;
}
// 扣减秒杀活动的库存
seckillActivityApi.updateSeckillStock(order.getSeckillActivityId(),
orderItems.get(0).getSkuId(), orderItems.get(0).getCount());
}
@Override
public void cancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
if (TradeOrderTypeEnum.isSeckill(order.getType())) {
return;
}
}
}

View File

@ -68,7 +68,6 @@ public class TradePriceCalculateReqBO {
private Long seckillActivityId;
// ========== 拼团活动相关字段 ==========
// TODO @puhui999是不是拼团记录的编号哈
/**
* 拼团活动编号
*/

View File

@ -12,6 +12,8 @@ import org.springframework.stereotype.Component;
import javax.annotation.Resource;
// TODO @puhui999单测可以后补下
/**
* 拼团活动的 {@link TradePriceCalculator} 实现类
*
@ -30,12 +32,11 @@ public class TradeCombinationActivityPriceCalculator implements TradePriceCalcul
if (param.getCombinationActivityId() == null) {
return;
}
Assert.isTrue(param.getItems().size() == 1, "拼团时,只允许选择一个商品");
// 2. 校验是否可以参与拼团
TradePriceCalculateRespBO.OrderItem orderItem = result.getItems().get(0);
CombinationValidateJoinRespDTO combinationActivity = combinationRecordApi.validateJoinCombination(
param.getCombinationActivityId(), param.getUserId(),
param.getUserId(), param.getCombinationActivityId(), param.getCombinationHeadId(),
orderItem.getSkuId(), orderItem.getCount());
// 3.1 记录优惠明细

View File

@ -176,7 +176,7 @@ public class DeptServiceImpl implements DeptService {
}
@Override
@DataPermission(enable = false) // 禁用数据权限避免简历不正确的缓存
@DataPermission(enable = false) // 禁用数据权限避免建立不正确的缓存
@Cacheable(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST, key = "#id")
public Set<Long> getChildDeptIdListFromCache(Long id) {
List<DeptDO> children = getChildDeptList(id);