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:
commit
74a5e40d8d
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
/**
|
||||
* 拼团商品单价
|
||||
|
|
|
@ -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, "拼团失败,原因:单次限购超出");
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
/**
|
||||
* 活动状态
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 不匹配
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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, "售后单不存在");
|
||||
|
|
|
@ -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, "到期未收货,系统自动确认收货"),
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -52,7 +52,6 @@ public class AppTradeOrderSettlementReqVO {
|
|||
private Long seckillActivityId;
|
||||
|
||||
// ========== 拼团活动相关字段 ==========
|
||||
// TODO @puhui999:是不是拼团记录的编号哈?
|
||||
@Schema(description = "拼团活动编号", example = "1024")
|
||||
private Long combinationActivityId;
|
||||
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建单个订单的评论
|
||||
*
|
||||
|
|
|
@ -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 芋艿:取消订单时,需要增加库存
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -68,7 +68,6 @@ public class TradePriceCalculateReqBO {
|
|||
private Long seckillActivityId;
|
||||
|
||||
// ========== 拼团活动相关字段 ==========
|
||||
// TODO @puhui999:是不是拼团记录的编号哈?
|
||||
/**
|
||||
* 拼团活动编号
|
||||
*/
|
||||
|
|
|
@ -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 记录优惠明细
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue