!643 订单:完善拼团活动和一些 TODO

Merge pull request !643 from puhui999/feature/mall_product
This commit is contained in:
芋道源码 2023-10-01 13:56:43 +00:00 committed by Gitee
commit 4f7d6b4959
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
42 changed files with 606 additions and 284 deletions

View File

@ -97,4 +97,44 @@ public class LocalDateTimeUtils {
LocalDateTime.of(nowDate, startTime2), LocalDateTime.of(nowDate, endTime2));
}
/**
* 构建日期时间 TODO 后面有需要的话再继续扩展
*
* @author HUIHUI
*/
public static class BuilderDateTime {
/**
* 日期2023-10-01
*/
private String localDate;
/**
* 时间10:01:00
*/
private String localTime;
public BuilderDateTime() {
}
public BuilderDateTime withDate(String date) {
this.localDate = date;
return this;
}
public BuilderDateTime withDate(LocalDateTime date) {
this.localDate = LocalDateTimeUtil.format(date, "yyyy-MM-dd");
return this;
}
public BuilderDateTime withTime(String time) {
this.localTime = time;
return this;
}
public LocalDateTime build() {
return LocalDateTimeUtil.parse(this.localDate + " " + this.localTime, "yyyy-MM-dd HH:mm:ss");
}
}
}

View File

@ -20,13 +20,4 @@ public interface BargainRecordApi {
*/
void createBargainRecord(@Valid BargainRecordCreateReqDTO reqDTO);
/**
* 查询砍价是否成功
*
* @param userId 用户编号
* @param orderId 订单编号
* @return 砍价是否成功
*/
boolean isBargainRecordSuccess(Long userId, Long orderId);
}

View File

@ -7,14 +7,4 @@ package cn.iocoder.yudao.module.promotion.api.combination;
*/
public interface CombinationActivityApi {
/**
* 校验是否满足拼团条件
*
* @param activityId 活动编号
* @param userId 用户编号
* @param skuId sku 编号
* @param count 数量
*/
void validateCombination(Long activityId, Long userId, Long skuId, Integer count);
}

View File

@ -14,6 +14,16 @@ import java.time.LocalDateTime;
*/
public interface CombinationRecordApi {
/**
* 校验是否满足拼团条件
*
* @param activityId 活动编号
* @param userId 用户编号
* @param skuId sku 编号
* @param count 数量
*/
void validateCombinationRecord(Long activityId, Long userId, Long skuId, Integer count);
/**
* 创建开团记录
*

View File

@ -28,6 +28,11 @@ public class CombinationRecordCreateReqDTO {
*/
@NotNull(message = "sku 编号不能为空")
private Long skuId;
/**
* 购买的商品数量
*/
@NotNull(message = "购买数量不能为空")
private Integer count;
/**
* 订单编号
*/

View File

@ -1,5 +1,10 @@
package cn.iocoder.yudao.module.promotion.api.seckill;
import cn.iocoder.yudao.module.promotion.api.seckill.dto.SeckillActivityProductRespDTO;
import java.util.Collection;
import java.util.List;
/**
* 秒杀活动 API 接口
*
@ -16,4 +21,13 @@ public interface SeckillActivityApi {
*/
void updateSeckillStock(Long id, Long skuId, Integer count);
/**
* 获取秒杀活动商品信息
*
* @param id 活动编号
* @param skuIds sku 编号
* @return 秒杀活动商品信息列表
*/
List<SeckillActivityProductRespDTO> getSeckillActivityProductList(Long id, Collection<Long> skuIds);
}

View File

@ -0,0 +1,65 @@
package cn.iocoder.yudao.module.promotion.api.seckill.dto;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
/**
* 秒杀活动商品 Response DTO
*
* @author HUIHUI
*/
@Data
public class SeckillActivityProductRespDTO {
/**
* 秒杀参与商品编号
*/
private Long id;
/**
* 秒杀活动 id
*
* 关联 SeckillActivityDO#getId()
*/
private Long activityId;
/**
* 秒杀时段 id
*
* 关联 SeckillConfigDO#getId()
*/
private List<Long> configIds;
/**
* 商品 SPU 编号
*/
private Long spuId;
/**
* 商品 SKU 编号
*/
private Long skuId;
/**
* 秒杀金额单位
*/
private Integer seckillPrice;
/**
* 秒杀库存
*/
private Integer stock;
/**
* 秒杀商品状态
*
* 枚举 {@link CommonStatusEnum 对应的类}
*/
private Integer activityStatus;
/**
* 活动开始时间点
*/
private LocalDateTime activityStartTime;
/**
* 活动结束时间点
*/
private LocalDateTime activityEndTime;
}

View File

@ -75,9 +75,11 @@ public interface ErrorCodeConstants {
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_TIME_END = new ErrorCode(1_013_011_005, "拼团失败,活动已经结束");
ErrorCode COMBINATION_RECORD_FAILED_SINGLE_LIMIT_COUNT_EXCEED = new ErrorCode(1_013_011_006, "拼团失败,原因:单次限购超出");
ErrorCode COMBINATION_RECORD_FAILED_TOTAL_LIMIT_COUNT_EXCEED = new ErrorCode(1_013_011_007, "拼团失败,原因:超出总购买次数");
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, "拼团失败,原因:单次限购超出");
ErrorCode COMBINATION_RECORD_FAILED_TOTAL_LIMIT_COUNT_EXCEED = new ErrorCode(1_013_011_008, "拼团失败,原因:超出总购买次数");
ErrorCode COMBINATION_RECORD_FAILED_ORDER_STATUS_UNPAID = new ErrorCode(1_013_011_009, "拼团失败,原因:存在未支付订单,请先支付");
// ========== 砍价活动 1-013-012-000 ==========
ErrorCode BARGAIN_ACTIVITY_NOT_EXISTS = new ErrorCode(1_013_012_000, "砍价活动不存在");

View File

@ -16,9 +16,4 @@ public class BargainRecordApiImpl implements BargainRecordApi {
}
@Override
public boolean isBargainRecordSuccess(Long userId, Long orderId) {
return false;
}
}

View File

@ -1,10 +1,7 @@
package cn.iocoder.yudao.module.promotion.api.combination;
import cn.iocoder.yudao.module.promotion.service.combination.CombinationActivityService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* 拼团活动 Api 接口实现类
*
@ -13,12 +10,4 @@ import javax.annotation.Resource;
@Service
public class CombinationActivityApiImpl implements CombinationActivityApi {
@Resource
private CombinationActivityService activityService;
@Override
public void validateCombination(Long activityId, Long userId, Long skuId, Integer count) {
activityService.validateCombination(activityId, userId, skuId, count);
}
}

View File

@ -19,6 +19,11 @@ public class CombinationRecordApiImpl implements CombinationRecordApi {
@Resource
private CombinationRecordService recordService;
@Override
public void validateCombinationRecord(Long activityId, Long userId, Long skuId, Integer count) {
recordService.validateCombinationRecord(activityId, userId, skuId, count);
}
@Override
public void createCombinationRecord(CombinationRecordCreateReqDTO reqDTO) {
recordService.createCombinationRecord(reqDTO);

View File

@ -1,9 +1,13 @@
package cn.iocoder.yudao.module.promotion.api.seckill;
import cn.iocoder.yudao.module.promotion.api.seckill.dto.SeckillActivityProductRespDTO;
import cn.iocoder.yudao.module.promotion.convert.seckill.seckillactivity.SeckillActivityConvert;
import cn.iocoder.yudao.module.promotion.service.seckill.SeckillActivityService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Collection;
import java.util.List;
/**
* 秒杀活动接口 Api 接口实现类
@ -21,4 +25,9 @@ public class SeckillActivityApiImpl implements SeckillActivityApi {
activityService.updateSeckillStock(id, skuId, count);
}
@Override
public List<SeckillActivityProductRespDTO> getSeckillActivityProductList(Long id, Collection<Long> skuIds) {
return SeckillActivityConvert.INSTANCE.convertList4(activityService.getSeckillActivityProductList(id, skuIds));
}
}

View File

@ -97,8 +97,8 @@ public interface CombinationActivityConvert {
default CombinationRecordDO convert(CombinationRecordCreateReqDTO reqDTO,
CombinationActivityDO activity, MemberUserRespDTO user,
ProductSpuRespDTO spu, ProductSkuRespDTO sku) {
// TODO @puhui999订单付款后需要设置开始时间和结束时间
return convert(reqDTO)
.setCount(reqDTO.getCount())
.setVirtualGroup(false)
.setExpireTime(activity.getStartTime().plusHours(activity.getLimitDuration()))
.setUserSize(activity.getUserSize())

View File

@ -1,12 +1,13 @@
package cn.iocoder.yudao.module.promotion.convert.seckill.seckillactivity;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils;
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
import cn.iocoder.yudao.module.product.enums.DictTypeConstants;
import cn.iocoder.yudao.module.promotion.api.seckill.dto.SeckillActivityProductRespDTO;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityCreateReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityDetailRespVO;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityRespVO;
@ -128,11 +129,16 @@ public interface SeckillActivityConvert {
default AppSeckillActivityDetailRespVO convert3(SeckillActivityDO seckillActivity, List<SeckillProductDO> products, SeckillConfigDO filteredConfig) {
return convert2(seckillActivity)
.setProducts(convertList1(products))
// TODO @puhui999要不要在里面 default 一个方法处理这个事件简洁一点
.setStartTime(LocalDateTimeUtil.parse(LocalDateTimeUtil.format(seckillActivity.getStartTime(), "yyyy-MM-dd") + " " + filteredConfig.getStartTime(),
"yyyy-MM-dd HH:mm:ss")) // 活动开始日期和时段结合
.setEndTime(LocalDateTimeUtil.parse(LocalDateTimeUtil.format(seckillActivity.getEndTime(), "yyyy-MM-dd") + " " + filteredConfig.getEndTime(),
"yyyy-MM-dd HH:mm:ss")); // 活动结束日期和时段结合
.setStartTime(new LocalDateTimeUtils.BuilderDateTime()
.withDate(seckillActivity.getStartTime())
.withTime(filteredConfig.getStartTime())
.build())// 活动开始日期和时段结合
.setEndTime(new LocalDateTimeUtils.BuilderDateTime()
.withDate(seckillActivity.getEndTime())
.withTime(filteredConfig.getEndTime())
.build()); // 活动结束日期和时段结合
}
List<SeckillActivityProductRespDTO> convertList4(List<SeckillProductDO> seckillActivityProductList);
}

View File

@ -62,6 +62,10 @@ public class CombinationRecordDO extends BaseDO {
* SKU 编号
*/
private Long skuId;
/**
* 购买的商品数量
*/
private Integer count;
/**
* 用户编号

View File

@ -20,6 +20,10 @@ 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)

View File

@ -116,10 +116,10 @@ public class BargainActivityServiceImpl implements BargainActivityService {
public void deleteBargainActivity(Long id) {
// 校验存在
BargainActivityDO activityDO = validateBargainActivityExists(id);
// 校验状态 TODO puhui: 测试完成后需要恢复校验
//if (ObjectUtil.equal(activityDO.getStatus(), CommonStatusEnum.ENABLE.getStatus())) {
// throw exception(BARGAIN_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END);
//}
// 校验状态
if (ObjectUtil.equal(activityDO.getStatus(), CommonStatusEnum.ENABLE.getStatus())) {
throw exception(BARGAIN_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END);
}
// 删除
bargainActivityMapper.deleteById(id);

View File

@ -84,17 +84,6 @@ public interface CombinationActivityService {
*/
List<CombinationProductDO> getCombinationProductsByActivityIds(Collection<Long> activityIds);
/**
* 校验是否满足拼团条件
* 如果不满足会抛出异常
*
* @param activityId 活动编号
* @param userId 用户编号
* @param skuId sku 编号
* @param count 数量
*/
void validateCombination(Long activityId, Long userId, Long skuId, Integer count);
/**
* 获取正在进行的活动分页数据
*

View File

@ -17,12 +17,8 @@ import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product
import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.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.dal.mysql.combination.CombinationActivityMapper;
import cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationProductMapper;
import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
import cn.iocoder.yudao.module.trade.api.order.TradeOrderApi;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
@ -33,7 +29,8 @@ import java.util.List;
import java.util.Map;
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.convertMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_EXISTS;
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
@ -53,16 +50,10 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
@Resource
private CombinationProductMapper combinationProductMapper;
@Resource
@Lazy // TODO @puhui999我感觉 validateCombination 可以挪到 CombinationRecordServiceImpl 因为它更偏向能不能创建拼团记录
private CombinationRecordService combinationRecordService;
@Resource
private ProductSpuApi productSpuApi;
@Resource
private ProductSkuApi productSkuApi;
@Resource
private TradeOrderApi tradeOrderApi;
@Override
@Transactional(rollbackFor = Exception.class)
@ -215,36 +206,6 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
return combinationProductMapper.selectListByActivityIds(activityIds);
}
@Override
public void validateCombination(Long activityId, Long userId, Long skuId, Integer count) {
// 1.1 校验拼团活动是否存在
CombinationActivityDO activity = validateCombinationActivityExists(activityId);
// 1.2 校验活动是否开启
if (ObjectUtil.equal(activity.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {
throw exception(COMBINATION_ACTIVITY_STATUS_DISABLE);
}
// 1.3 校验是否超出单次限购数量
if (count > activity.getSingleLimitCount()) {
throw exception(COMBINATION_RECORD_FAILED_SINGLE_LIMIT_COUNT_EXCEED);
}
// 2. 校验是否超出总限购数量
List<CombinationRecordDO> recordList = combinationRecordService.getRecordListByUserIdAndActivityId(userId, activityId);
if (CollUtil.isEmpty(recordList)) {
return;
}
// 过滤出拼团成功的
// TODO @puhui999count 要不存一个在 record
List<Long> skuIds = convertList(recordList, CombinationRecordDO::getSkuId,
item -> ObjectUtil.equals(item.getStatus(), CombinationRecordStatusEnum.SUCCESS.getStatus()));
Integer countSum = tradeOrderApi.getOrderItemCountSumByOrderIdAndSkuId(convertList(recordList,
CombinationRecordDO::getOrderId,
item -> ObjectUtil.equals(item.getStatus(), CombinationRecordStatusEnum.SUCCESS.getStatus())), skuIds);
if (activity.getTotalLimitCount() < countSum) {
throw exception(COMBINATION_RECORD_FAILED_TOTAL_LIMIT_COUNT_EXCEED);
}
}
@Override
public List<CombinationActivityDO> getCombinationActivityListByCount(Integer count) {
return combinationActivityMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus(), count);

View File

@ -22,6 +22,17 @@ public interface CombinationRecordService {
*/
void updateCombinationRecordStatusByUserIdAndOrderId(Integer status, Long userId, Long orderId);
/**
* 校验是否满足拼团条件
* 如果不满足会抛出异常
*
* @param activityId 活动编号
* @param userId 用户编号
* @param skuId sku 编号
* @param count 数量
*/
void validateCombinationRecord(Long activityId, Long userId, Long skuId, Integer count);
/**
* 创建拼团记录
*

View File

@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.promotion.service.combination;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
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;
@ -14,6 +15,8 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationA
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
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;
@ -24,6 +27,7 @@ import java.time.LocalDateTime;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
// TODO 芋艿等拼团记录做完完整 review
@ -51,6 +55,8 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
@Resource
@Lazy
private ProductSkuApi productSkuApi;
@Resource
private TradeOrderApi tradeOrderApi;
@Override
@Transactional(rollbackFor = Exception.class)
@ -96,30 +102,77 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
return recordDO;
}
// TODO @puhui999有一个应该在创建那要做下就是当前 activityId 已经有未支付的订单不允许在发起新的要么支付要么去掉先
@Override
public void validateCombinationRecord(Long activityId, Long userId, Long skuId, Integer count) {
// 1.1 校验拼团活动是否存在
CombinationActivityDO activity = combinationActivityService.validateCombinationActivityExists(activityId);
// 1.2 校验活动是否开启
if (ObjectUtil.equal(activity.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {
throw exception(COMBINATION_ACTIVITY_STATUS_DISABLE);
}
// 2 校验是否超出单次限购数量
if (count > activity.getSingleLimitCount()) {
throw exception(COMBINATION_RECORD_FAILED_SINGLE_LIMIT_COUNT_EXCEED);
}
// 3校验是否有拼团记录
List<CombinationRecordDO> recordList = getRecordListByUserIdAndActivityId(userId, activityId);
if (CollUtil.isEmpty(recordList)) {
return;
}
// 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()) {
throw exception(COMBINATION_RECORD_FAILED_TOTAL_LIMIT_COUNT_EXCEED);
}
// 5校验拼团记录是否存在未支付的订单如果存在未支付的订单则不允许发起新的拼团
CombinationRecordDO record = findFirst(recordList, item -> ObjectUtil.equals(item.getStatus(), null));
if (record == null) {
return;
}
// 5.1查询关联的订单是否已经支付
// 当前 activityId 已经有未支付的订单不允许在发起新的要么支付要么去掉先
Integer orderStatus = tradeOrderApi.getOrderStatus(record.getOrderId());
if (ObjectUtil.equal(orderStatus, TradeOrderStatusEnum.UNPAID.getStatus())) {
throw exception(COMBINATION_RECORD_FAILED_ORDER_STATUS_UNPAID);
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void createCombinationRecord(CombinationRecordCreateReqDTO reqDTO) {
// 1.1 校验拼团活动
// 1.1 校验拼团活动
CombinationActivityDO activity = combinationActivityService.validateCombinationActivityExists(reqDTO.getActivityId());
// 1.2 需要校验下他当前是不是已经参加了该拼团
// TODO @puhui999拼团应该可以重复参加应该去校验总共的上限哈就是 activity.totalLimitCount
CombinationRecordDO recordDO = recordMapper.selectByUserIdAndOrderId(reqDTO.getUserId(), reqDTO.getOrderId());
if (recordDO != null) {
throw exception(COMBINATION_RECORD_EXISTS);
// 1.2 校验是否超出单次限购数量
if (reqDTO.getCount() > activity.getSingleLimitCount()) {
throw exception(COMBINATION_RECORD_FAILED_SINGLE_LIMIT_COUNT_EXCEED);
}
// 1.3 校验用户是否参加了其它拼团
// 1.3校验是否有拼团记录
List<CombinationRecordDO> records = getRecordListByUserIdAndActivityId(reqDTO.getUserId(), reqDTO.getActivityId());
if (CollUtil.isEmpty(records)) {
return;
}
// 1.4校验是否超出总限购数量
Integer sumValue = getSumValue(convertList(records, CombinationRecordDO::getCount,
item -> ObjectUtil.equals(item.getStatus(), CombinationRecordStatusEnum.SUCCESS.getStatus())), i -> i, Integer::sum);
if ((sumValue + reqDTO.getCount()) > activity.getTotalLimitCount()) {
throw exception(COMBINATION_RECORD_FAILED_TOTAL_LIMIT_COUNT_EXCEED);
}
// 2 校验用户是否参加了其它拼团
List<CombinationRecordDO> recordDOList = recordMapper.selectListByUserIdAndStatus(reqDTO.getUserId(), CombinationRecordStatusEnum.IN_PROGRESS.getStatus());
if (CollUtil.isNotEmpty(recordDOList)) {
throw exception(COMBINATION_RECORD_FAILED_HAVE_JOINED);
}
// TODO @puhui999有个开始时间未校验
// 1.4 校验当前活动是否过期
// 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);
}
// 1.5 父拼团是否存在,是否已经满了
// 5 父拼团是否存在,是否已经满了
if (reqDTO.getHeadId() != null) {
// 查询进行中的父拼团
CombinationRecordDO record = recordMapper.selectOneByHeadId(reqDTO.getHeadId(), CombinationRecordStatusEnum.IN_PROGRESS.getStatus());
@ -132,8 +185,6 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
}
}
// TODO @puhui999单次限购
// 2. 创建拼团记录
MemberUserRespDTO user = memberUserApi.getUser(reqDTO.getUserId());
ProductSpuRespDTO spu = productSpuApi.getSpu(reqDTO.getSpuId());

View File

@ -37,9 +37,9 @@ public interface SeckillActivityService {
/**
* 更新秒杀库存
*
* @param id 活动编号
* @param skuId sku 编号
* @param count 数量
* @param id 活动编号
* @param skuId sku 编号
* @param count 数量
*/
void updateSeckillStock(Long id, Long skuId, Integer count);
@ -114,4 +114,13 @@ public interface SeckillActivityService {
*/
PageResult<SeckillActivityDO> getSeckillActivityAppPageByConfigId(AppSeckillActivityPageReqVO pageReqVO);
/**
* 获取秒杀活动商品信息
*
* @param id 活动编号
* @param skuIds sku 编号
* @return 秒杀活动商品信息列表
*/
List<SeckillProductDO> getSeckillActivityProductList(Long id, Collection<Long> skuIds);
}

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.promotion.service.seckill;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
@ -278,4 +279,18 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
return seckillActivityMapper.selectPage(pageReqVO, CommonStatusEnum.ENABLE.getStatus());
}
@Override
public List<SeckillProductDO> getSeckillActivityProductList(Long id, Collection<Long> skuIds) {
// 1校验秒杀活动是否存在
validateSeckillActivityExists(id);
// 2校验活动商品是否存在
List<SeckillProductDO> productList = filterList(seckillProductMapper.selectListByActivityId(id),
item -> skuIds.contains(item.getSkuId()));
if (CollectionUtil.isEmpty(productList)) {
throw exception(SKU_NOT_EXISTS);
}
return productList;
}
}

View File

@ -1,7 +1,5 @@
package cn.iocoder.yudao.module.trade.api.order;
import java.util.Collection;
/**
* 订单 API 接口
*
@ -10,21 +8,11 @@ import java.util.Collection;
public interface TradeOrderApi {
/**
* 验证订单
* 获取订单状态
*
* @param userId 用户 id
* @param orderItemId 订单项 id
* @return 校验通过返回订单 id
* @param id 订单编号
* @return 订单状态
*/
Long validateOrder(Long userId, Long orderItemId);
/**
* 获取订单项商品购买数量总和
*
* @param orderIds 订单编号
* @param skuIds sku 编号
* @return 订单项商品购买数量总和
*/
Integer getOrderItemCountSumByOrderIdAndSkuId(Collection<Long> orderIds, Collection<Long> skuIds);
Integer getOrderStatus(Long id);
}

View File

@ -1,15 +1,14 @@
package cn.iocoder.yudao.module.trade.api.order;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.util.Collection;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.ORDER_ITEM_NOT_FOUND;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.ORDER_NOT_FOUND;
/**
* 订单 API 接口实现类
@ -24,18 +23,12 @@ public class TradeOrderApiImpl implements TradeOrderApi {
private TradeOrderQueryService tradeOrderQueryService;
@Override
public Long validateOrder(Long userId, Long orderItemId) {
// 校验订单项订单项存在订单就存在
TradeOrderItemDO item = tradeOrderQueryService.getOrderItem(userId, orderItemId);
if (item == null) {
throw exception(ORDER_ITEM_NOT_FOUND);
public Integer getOrderStatus(Long id) {
TradeOrderDO order = tradeOrderQueryService.getOrder(id);
if (order == null) {
throw exception(ORDER_NOT_FOUND);
}
return item.getOrderId();
}
@Override
public Integer getOrderItemCountSumByOrderIdAndSkuId(Collection<Long> orderIds, Collection<Long> skuIds) {
return tradeOrderQueryService.getOrderItemCountSumByOrderIdAndSkuId(orderIds, skuIds);
return order.getStatus();
}
}

View File

@ -16,6 +16,7 @@ import java.util.List;
@Schema(description = "用户 App - 交易订单结算 Request VO")
@Data
@Valid
public class AppTradeOrderSettlementReqVO {
@Schema(description = "商品项数组", requiredMode = Schema.RequiredMode.REQUIRED)
@ -62,7 +63,16 @@ public class AppTradeOrderSettlementReqVO {
@Schema(description = "砍价活动编号", example = "123")
private Long bargainActivityId;
// TODO @puhui999可以写个参数校验如果 seckillActivityId combinationActivityId combinationHeadId 的情况items 应该只有一个
@AssertTrue(message = "活动商品每次只能购买一种规格")
@JsonIgnore
public boolean isValidActivityItems() {
// 校验是否是活动订单
if (seckillActivityId == null && combinationActivityId == null && combinationHeadId == null) {
return true;
}
// 校验订单项是否超出
return items.size() == 1;
}
@Data
@Schema(description = "用户 App - 商品项")
@ -70,7 +80,9 @@ public class AppTradeOrderSettlementReqVO {
public static class Item {
@Schema(description = "商品 SKU 编号", example = "2048")
@NotNull(message = "商品 SKU 编号不能为空")
private Long skuId;
@Schema(description = "购买数量", example = "1")
@Min(value = 1, message = "购买数量最小值为 {value}")
private Integer count;

View File

@ -93,6 +93,7 @@ public interface TradeOrderConvert {
new ProductSkuUpdateStockReqDTO.Item().setId(item.getSkuId()).setIncrCount(item.getCount()));
return new ProductSkuUpdateStockReqDTO(items);
}
default ProductSkuUpdateStockReqDTO convertNegative(List<AppTradeOrderSettlementReqVO.Item> list) {
List<ProductSkuUpdateStockReqDTO.Item> items = CollectionUtils.convertList(list, item ->
new ProductSkuUpdateStockReqDTO.Item().setId(item.getSkuId()).setIncrCount(-item.getCount()));
@ -209,6 +210,8 @@ public interface TradeOrderConvert {
})
ProductCommentCreateReqDTO convert04(AppTradeOrderItemCommentCreateReqVO createReqVO, TradeOrderItemDO tradeOrderItemDO);
TradePriceCalculateReqBO convert(AppTradeOrderSettlementReqVO settlementReqVO);
default TradePriceCalculateReqBO convert(Long userId, AppTradeOrderSettlementReqVO settlementReqVO,
List<CartDO> cartList) {
TradePriceCalculateReqBO reqBO = new TradePriceCalculateReqBO().setUserId(userId)
@ -249,17 +252,6 @@ public interface TradeOrderConvert {
AppTradeOrderSettlementRespVO convert0(TradePriceCalculateRespBO calculate, AddressRespDTO address);
@Mappings({
@Mapping(target = "activityId", source = "afterOrderCreateReqBO.combinationActivityId"),
@Mapping(target = "spuId", source = "afterOrderCreateReqBO.spuId"),
@Mapping(target = "skuId", source = "afterOrderCreateReqBO.skuId"),
@Mapping(target = "orderId", source = "afterOrderCreateReqBO.orderId"),
@Mapping(target = "userId", source = "afterOrderCreateReqBO.userId"),
@Mapping(target = "headId", source = "afterOrderCreateReqBO.combinationHeadId"),
@Mapping(target = "combinationPrice", source = "afterOrderCreateReqBO.payPrice"),
})
CombinationRecordCreateReqDTO convert(TradeAfterOrderCreateReqBO afterOrderCreateReqBO);
List<AppOrderExpressTrackRespDTO> convertList02(List<ExpressTrackRespDTO> list);
TradeOrderDO convert(TradeOrderUpdateAddressReqVO reqVO);
@ -280,18 +272,36 @@ public interface TradeOrderConvert {
return bo;
}
TradeBeforeOrderCreateReqBO convert(AppTradeOrderCreateReqVO createReqVO);
@Mappings({
@Mapping(target = "userId", source = "userId"),
@Mapping(target = "orderType", source = "calculateRespBO.type"),
@Mapping(target = "items", source = "createReqVO.items"),
})
TradeBeforeOrderCreateReqBO convert(Long userId, AppTradeOrderCreateReqVO createReqVO, TradePriceCalculateRespBO calculateRespBO);
List<TradeAfterOrderCreateReqBO.Item> convertList(List<TradeOrderItemDO> orderItems);
@Mappings({
@Mapping(target = "combinationActivityId", source = "createReqVO.combinationActivityId"),
@Mapping(target = "combinationHeadId", source = "createReqVO.combinationHeadId"),
@Mapping(target = "spuId", source = "orderItem.spuId"),
@Mapping(target = "skuId", source = "orderItem.skuId"),
@Mapping(target = "orderId", source = "tradeOrderDO.id"),
@Mapping(target = "userId", source = "userId"),
@Mapping(target = "orderId", source = "tradeOrderDO.id"),
@Mapping(target = "payPrice", source = "tradeOrderDO.payPrice"),
@Mapping(target = "items", source = "orderItems"),
})
TradeAfterOrderCreateReqBO convert(Long userId, AppTradeOrderCreateReqVO createReqVO,
TradeOrderDO tradeOrderDO, TradeOrderItemDO orderItem);
TradeOrderDO tradeOrderDO, List<TradeOrderItemDO> orderItems);
@Mappings({
@Mapping(target = "activityId", source = "combinationActivityId"),
@Mapping(target = "spuId", expression = "java(reqBO.getItems().get(0).getSpuId())"),
@Mapping(target = "skuId", expression = "java(reqBO.getItems().get(0).getSkuId())"),// TODO 艿艿看看这里
@Mapping(target = "count", expression = "java(reqBO.getItems().get(0).getCount())"),
@Mapping(target = "orderId", source = "orderId"),
@Mapping(target = "userId", source = "userId"),
@Mapping(target = "headId", source = "combinationHeadId"),
@Mapping(target = "combinationPrice", source = "payPrice")
})
CombinationRecordCreateReqDTO convert(TradeAfterOrderCreateReqBO reqBO);
}

View File

@ -26,12 +26,6 @@ public interface TradeOrderItemMapper extends BaseMapperX<TradeOrderItemDO> {
return selectList(TradeOrderItemDO::getOrderId, orderIds);
}
default List<TradeOrderItemDO> selectListByOrderIdAnSkuId(Collection<Long> orderIds, Collection<Long> skuIds) {
return selectList(new LambdaQueryWrapperX<TradeOrderItemDO>()
.in(TradeOrderItemDO::getOrderId, orderIds)
.in(TradeOrderItemDO::getSkuId, skuIds));
}
default TradeOrderItemDO selectByIdAndUserId(Long orderItemId, Long loginUserId) {
return selectOne(new LambdaQueryWrapperX<TradeOrderItemDO>()
.eq(TradeOrderItemDO::getId, orderItemId)

View File

@ -119,13 +119,5 @@ public interface TradeOrderQueryService {
*/
List<TradeOrderItemDO> getOrderItemListByOrderId(Collection<Long> orderIds);
/**
* 获取订单项商品购买数量总和
*
* @param orderIds 订单编号
* @param skuIds sku 编号
* @return 订单项商品购买数量总和
*/
Integer getOrderItemCountSumByOrderIdAndSkuId(Collection<Long> orderIds, Collection<Long> skuIds);
}

View File

@ -4,7 +4,6 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageReqVO;
@ -169,10 +168,4 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService {
return tradeOrderItemMapper.selectListByOrderId(orderIds);
}
@Override
public Integer getOrderItemCountSumByOrderIdAndSkuId(Collection<Long> orderIds, Collection<Long> skuIds) {
List<TradeOrderItemDO> tradeOrderItems = tradeOrderItemMapper.selectListByOrderIdAnSkuId(orderIds, skuIds);
return CollectionUtils.getSumValue(tradeOrderItems, TradeOrderItemDO::getCount, Integer::sum);
}
}

View File

@ -59,7 +59,7 @@ import cn.iocoder.yudao.module.trade.service.cart.CartService;
import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService;
import cn.iocoder.yudao.module.trade.service.message.TradeMessageService;
import cn.iocoder.yudao.module.trade.service.message.bo.TradeOrderMessageWhenDeliveryOrderReqBO;
import cn.iocoder.yudao.module.trade.service.order.bo.TradeBeforeOrderCreateReqBO;
import cn.iocoder.yudao.module.trade.service.order.bo.TradeAfterPayOrderReqBO;
import cn.iocoder.yudao.module.trade.service.order.handler.TradeOrderHandler;
import cn.iocoder.yudao.module.trade.service.price.TradePriceService;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
@ -253,12 +253,9 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
private void beforeCreateTradeOrder(Long userId, AppTradeOrderCreateReqVO createReqVO,
TradePriceCalculateRespBO calculateRespBO) {
// 1. 执行订单创建前置处理器
TradeBeforeOrderCreateReqBO beforeOrderCreateReqBO = TradeOrderConvert.INSTANCE.convert(createReqVO);
beforeOrderCreateReqBO.setOrderType(calculateRespBO.getType());
beforeOrderCreateReqBO.setUserId(userId);
beforeOrderCreateReqBO.setCount(getSumValue(createReqVO.getItems(), AppTradeOrderSettlementReqVO.Item::getCount, Integer::sum));
// TODO @puhui999这里有个纠结点handler 的定义是只处理指定类型的订单的拓展逻辑还是通用的 handler类似可以处理优惠劵等等
tradeOrderHandlers.forEach(handler -> handler.beforeOrderCreate(beforeOrderCreateReqBO));
tradeOrderHandlers.forEach(handler ->
handler.beforeOrderCreate(TradeOrderConvert.INSTANCE.convert(userId, createReqVO, calculateRespBO)));
// 2. 下单时扣减商品库存
productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convertNegative(createReqVO.getItems()));
@ -278,9 +275,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
TradeOrderDO order, List<TradeOrderItemDO> orderItems,
TradePriceCalculateRespBO calculateRespBO) {
// 1. 执行订单创建后置处理器
// TODO @puhui999从通用性来说应该不用 orderItems.get(0)
tradeOrderHandlers.forEach(handler -> handler.afterOrderCreate(
TradeOrderConvert.INSTANCE.convert(userId, createReqVO, order, orderItems.get(0))));
TradeOrderConvert.INSTANCE.convert(userId, createReqVO, order, orderItems)));
// 2. 有使用优惠券时更新
// 不在前置扣减的原因是因为优惠劵要记录使用的订单号
@ -337,13 +333,9 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID);
}
// 3. 校验活动
// 1拼团活动
// TODO @puhui999这块也抽象到 handler
if (Objects.equals(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) {
// 更新拼团状态 TODO puhui999订单支付失败或订单支付过期删除这条拼团记录
combinationRecordApi.updateRecordStatusToInProgress(order.getUserId(), order.getId(), LocalDateTime.now());
}
// 3订单支付成功后
tradeOrderHandlers.forEach(tradeOrderHandler -> tradeOrderHandler.afterPayOrder(new TradeAfterPayOrderReqBO()
.setOrderId(order.getId()).setOrderType(order.getType()).setUserId(order.getUserId()).setPayTime(LocalDateTime.now())));
// 4.1 增加用户积分赠送
addUserPoint(order.getUserId(), order.getGivePoint(), MemberPointBizTypeEnum.ORDER_GIVE, order.getId());
@ -470,14 +462,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
throw exception(ORDER_DELIVERY_FAIL_COMBINATION_RECORD_STATUS_NOT_SUCCESS);
}
}
// 订单类类型砍价
if (Objects.equals(TradeOrderTypeEnum.BARGAIN.getType(), order.getType())) {
// 校验订单砍价是否成功
// TODO @puhui999砍价的话应该不用校验因为是砍价成功后才可以下单
if (!bargainRecordApi.isBargainRecordSuccess(order.getUserId(), order.getId())) {
throw exception(ORDER_DELIVERY_FAIL_BARGAIN_RECORD_STATUS_NOT_SUCCESS);
}
}
return order;
}
@ -644,7 +629,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
}
// 2. TODO 活动相关库存回滚需要活动 id活动 id 怎么获取app 端能否传过来回复从订单里拿呀
tradeOrderHandlers.forEach(handler -> handler.rollback());
tradeOrderHandlers.forEach(handler -> handler.cancelOrder());
// 3. 回滚库存
List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(id);

View File

@ -1,9 +1,10 @@
package cn.iocoder.yudao.module.trade.service.order.bo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.util.List;
// TODO 芋艿在想想这些参数的定义
/**
@ -16,25 +17,71 @@ public class TradeAfterOrderCreateReqBO {
// ========== 拼团活动相关字段 ==========
@Schema(description = "拼团活动编号", example = "1024")
/**
* 拼团活动编号
*/
private Long combinationActivityId;
@Schema(description = "拼团团长编号", example = "2048")
/**
* 拼团团长编号
*/
private Long combinationHeadId;
@NotNull(message = "SPU 编号不能为空")
private Long spuId;
@NotNull(message = "SKU 编号活动商品不能为空")
private Long skuId;
/**
* 订单编号
*/
@NotNull(message = "订单编号不能为空")
private Long orderId;
/**
* 用户编号
*/
@NotNull(message = "用户编号不能为空")
private Long userId;
/**
* 支付金额
*/
@NotNull(message = "支付金额不能为空")
private Integer payPrice;
// ========== 购买商品相关字段 ==========
/**
* 订单购买的商品信息
*/
private List<Item> items;
/**
* 订单商品信息
* 记录购买商品的简要核心信息
*
* @author HUIHUI
*/
@Data
@Valid
public static class Item {
/**
* SPU 编号
*/
@NotNull(message = "SPU 编号不能为空")
private Long spuId;
/**
* 商品 SKU 编号
*
* 关联 ProductSkuDO id 编号
*/
@NotNull(message = "SKU 编号活动商品不能为空")
private Long skuId;
/**
* 购买的商品数量
*/
@NotNull(message = "购买数量不能为空")
private Integer count;
}
}

View File

@ -0,0 +1,43 @@
package cn.iocoder.yudao.module.trade.service.order.bo;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 订单支付后 Request BO
*
* @author HUIHUI
*/
@Data
public class TradeAfterPayOrderReqBO {
/**
* 订单编号
*/
@Schema(description = "订单编号", example = "6")
private Long orderId;
/**
* 订单类型
*
* 枚举 {@link TradeOrderTypeEnum}
*/
@Schema(description = "订单类型", example = "3")
private Integer orderType;
/**
* 用户编号
*/
@Schema(description = "用户编号", example = "11")
private Long userId;
/**
* 订单支付时间
*/
@Schema(description = "订单支付时间", example = "2023-08-15 10:00:00")
private LocalDateTime payTime;
}

View File

@ -1,12 +1,13 @@
package cn.iocoder.yudao.module.trade.service.order.bo;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.util.List;
// TODO 芋艿在想想这些参数的定义
/**
* 订单创建之前 Request BO
*
@ -34,9 +35,8 @@ public class TradeBeforeOrderCreateReqBO {
// ========== 秒杀活动相关字段 ==========
/**
*
* 秒杀活动编号
*/
@Schema(description = "秒杀活动编号", example = "1024")
private Long seckillActivityId;
// ========== 拼团活动相关字段 ==========
@ -44,13 +44,11 @@ public class TradeBeforeOrderCreateReqBO {
/**
* 拼团活动编号
*/
@Schema(description = "拼团活动编号", example = "1024")
private Long combinationActivityId;
/**
* 拼团团长编号
*/
@Schema(description = "拼团团长编号", example = "2048")
private Long combinationHeadId;
// ========== 砍价活动相关字段 ==========
@ -58,31 +56,39 @@ public class TradeBeforeOrderCreateReqBO {
/**
* 砍价活动编号
*/
@Schema(description = "砍价活动编号", example = "123")
private Long bargainActivityId;
// ========== 活动购买商品相关字段 ==========
// ========== 购买商品相关字段 ==========
/**
* 商品 SPU 编号
* 订单购买的商品信息
*/
private List<Item> items;
/**
* 订单商品信息
* 记录购买商品的简要核心信息
*
* 关联 ProductSkuDO spuId 编号
* @author HUIHUI
*/
@NotNull(message = "SPU 编号不能为空")
private Long spuId;
@Data
@Valid
public static class Item {
/**
* 商品 SKU 编号
*
* 关联 ProductSkuDO id 编号
*/
@NotNull(message = "SKU 编号活动商品不能为空")
private Long skuId;
/**
* 商品 SKU 编号
*
* 关联 ProductSkuDO id 编号
*/
@NotNull(message = "SKU 编号活动商品不能为空")
private Long skuId;
/**
* 购买的商品数量
*/
@NotNull(message = "购买数量不能为空")
private Integer count;
/**
* 购买的商品数量
*/
@NotNull(message = "购买数量不能为空")
private Integer count;
}
}

View File

@ -3,7 +3,6 @@ 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.trade.enums.order.TradeOrderTypeEnum;
import cn.iocoder.yudao.module.trade.service.order.bo.TradeAfterOrderCreateReqBO;
import cn.iocoder.yudao.module.trade.service.order.bo.TradeBeforeOrderCreateReqBO;
import org.springframework.stereotype.Component;
@ -15,7 +14,7 @@ import javax.annotation.Resource;
* @author HUIHUI
*/
@Component
public class TradeBargainHandler implements TradeOrderHandler {
public class TradeBargainHandler extends TradeOrderDefaultHandler {
@Resource
private BargainActivityApi bargainActivityApi;
@ -30,18 +29,11 @@ public class TradeBargainHandler implements TradeOrderHandler {
if (ObjectUtil.notEqual(TradeOrderTypeEnum.BARGAIN.getType(), reqBO.getOrderType())) {
return;
}
// 获取商品信息
TradeBeforeOrderCreateReqBO.Item item = reqBO.getItems().get(0);
// 扣减砍价活动的库存
bargainActivityApi.updateBargainActivityStock(reqBO.getBargainActivityId(), reqBO.getCount());
}
@Override
public void afterOrderCreate(TradeAfterOrderCreateReqBO reqBO) {
}
@Override
public void rollback() {
bargainActivityApi.updateBargainActivityStock(reqBO.getBargainActivityId(), item.getCount());
}
}

View File

@ -1,11 +1,11 @@
package cn.iocoder.yudao.module.trade.service.order.handler;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.module.promotion.api.combination.CombinationActivityApi;
import cn.iocoder.yudao.module.promotion.api.combination.CombinationRecordApi;
import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
import cn.iocoder.yudao.module.trade.service.order.bo.TradeAfterOrderCreateReqBO;
import cn.iocoder.yudao.module.trade.service.order.bo.TradeAfterPayOrderReqBO;
import cn.iocoder.yudao.module.trade.service.order.bo.TradeBeforeOrderCreateReqBO;
import org.springframework.stereotype.Component;
@ -17,28 +17,27 @@ import javax.annotation.Resource;
* @author HUIHUI
*/
@Component
public class TradeCombinationHandler implements TradeOrderHandler {
public class TradeCombinationHandler extends TradeOrderDefaultHandler {
@Resource
private CombinationActivityApi combinationActivityApi;
@Resource
private CombinationRecordApi combinationRecordApi;
@Override
public void beforeOrderCreate(TradeBeforeOrderCreateReqBO reqBO) {
// 如果是拼团订单
// 如果不是拼团订单则结束
if (ObjectUtil.notEqual(TradeOrderTypeEnum.COMBINATION.getType(), reqBO.getOrderType())) {
return;
}
// 获取商品信息
TradeBeforeOrderCreateReqBO.Item item = reqBO.getItems().get(0);
// 校验是否满足拼团活动相关限制
combinationActivityApi.validateCombination(reqBO.getCombinationActivityId(), reqBO.getUserId(), reqBO.getSkuId(), reqBO.getCount());
combinationRecordApi.validateCombinationRecord(reqBO.getCombinationActivityId(), reqBO.getUserId(), item.getSkuId(), item.getCount());
}
@Override
public void afterOrderCreate(TradeAfterOrderCreateReqBO reqBO) {
// TODO @puhui999需要判断下
if (true) {
if (reqBO.getCombinationActivityId() == null) {
return;
}
@ -47,8 +46,14 @@ public class TradeCombinationHandler implements TradeOrderHandler {
}
@Override
public void rollback() {
public void afterPayOrder(TradeAfterPayOrderReqBO reqBO) {
// 如果不是拼团订单则结束
if (ObjectUtil.notEqual(TradeOrderTypeEnum.COMBINATION.getType(), reqBO.getOrderType())) {
return;
}
// 更新拼团状态 TODO puhui999订单支付失败或订单支付过期删除这条拼团记录
combinationRecordApi.updateRecordStatusToInProgress(reqBO.getUserId(), reqBO.getOrderId(), reqBO.getPayTime());
}
}

View File

@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.trade.service.order.handler;
import cn.iocoder.yudao.module.trade.service.order.bo.TradeAfterOrderCreateReqBO;
import cn.iocoder.yudao.module.trade.service.order.bo.TradeAfterPayOrderReqBO;
import cn.iocoder.yudao.module.trade.service.order.bo.TradeBeforeOrderCreateReqBO;
/**
* 订单活动特殊逻辑处理器 handler 默认抽象实现类
*
* @author HUIHUI
*/
public abstract class TradeOrderDefaultHandler implements TradeOrderHandler {
@Override
public void beforeOrderCreate(TradeBeforeOrderCreateReqBO reqBO) {
}
@Override
public void afterOrderCreate(TradeAfterOrderCreateReqBO reqBO) {
}
@Override
public void afterPayOrder(TradeAfterPayOrderReqBO reqBO) {
}
@Override
public void cancelOrder() {
}
}

View File

@ -1,10 +1,12 @@
package cn.iocoder.yudao.module.trade.service.order.handler;
import cn.iocoder.yudao.module.trade.service.order.bo.TradeAfterOrderCreateReqBO;
import cn.iocoder.yudao.module.trade.service.order.bo.TradeAfterPayOrderReqBO;
import cn.iocoder.yudao.module.trade.service.order.bo.TradeBeforeOrderCreateReqBO;
/**
* 订单活动特殊逻辑处理器 handler 接口
* 提供订单生命周期钩子接口订单创建前订单创建后订单支付后订单取消
*
* @author HUIHUI
*/
@ -24,12 +26,16 @@ public interface TradeOrderHandler {
*/
void afterOrderCreate(TradeAfterOrderCreateReqBO reqBO);
// TODO @puhui999这个搞成订单取消
/**
* 回滚
* 支付订单后
*
* @param reqBO 请求
*/
void rollback();
void afterPayOrder(TradeAfterPayOrderReqBO reqBO);
// TODO @puhui999再搞个订单项取消哈
/**
* 订单取消
*/
void cancelOrder();
}

View File

@ -3,7 +3,6 @@ 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.enums.order.TradeOrderTypeEnum;
import cn.iocoder.yudao.module.trade.service.order.bo.TradeAfterOrderCreateReqBO;
import cn.iocoder.yudao.module.trade.service.order.bo.TradeBeforeOrderCreateReqBO;
import org.springframework.stereotype.Component;
@ -15,7 +14,7 @@ import javax.annotation.Resource;
* @author HUIHUI
*/
@Component
public class TradeSeckillHandler implements TradeOrderHandler {
public class TradeSeckillHandler extends TradeOrderDefaultHandler {
@Resource
private SeckillActivityApi seckillActivityApi;
@ -30,18 +29,11 @@ public class TradeSeckillHandler implements TradeOrderHandler {
if (ObjectUtil.notEqual(TradeOrderTypeEnum.SECKILL.getType(), reqBO.getOrderType())) {
return;
}
// 获取商品信息
TradeBeforeOrderCreateReqBO.Item item = reqBO.getItems().get(0);
// 扣减秒杀活动的库存
seckillActivityApi.updateSeckillStock(reqBO.getSeckillActivityId(), reqBO.getSkuId(), reqBO.getCount());
}
@Override
public void afterOrderCreate(TradeAfterOrderCreateReqBO reqBO) {
}
@Override
public void rollback() {
seckillActivityApi.updateSeckillStock(reqBO.getSeckillActivityId(), item.getSkuId(), item.getCount());
}
}

View File

@ -98,9 +98,19 @@ public class TradePriceCalculateRespBO {
* VIP 减免金额单位
*/
private Integer vipPrice;
/**
* 秒杀拼团砍价活动商品的总金额单位
*
* 基于 {@link OrderItem#getActivityPrice()} ()} * {@link OrderItem#getCount()} 求和
*/
private Integer activityPrice;
/**
* 最终购买金额单位
*
* ==========活动情况===========
* = {@link #activityPrice}
* + {@link #deliveryPrice}
* ==========正常情况===========
* = {@link #totalPrice}
* - {@link #couponPrice}
* - {@link #pointPrice}
@ -176,9 +186,16 @@ public class TradePriceCalculateRespBO {
* VIP 减免金额单位
*/
private Integer vipPrice;
/**
* 秒杀拼团砍价活动商品的金额单位
*/
private Integer activityPrice;
/**
* 应付金额单位
*
* ==========活动情况===========
* = {@link #activityPrice} * {@link #count}
* + {@link #deliveryPrice}
* ==========正常情况===========
* = {@link #price} * {@link #count}
* - {@link #couponPrice}
* - {@link #pointPrice}

View File

@ -105,6 +105,9 @@ public class TradePriceCalculatorHelper {
if (!item.getSelected()) {
return;
}
// TODO puhui: 需要在这里计算活动的价格
// ========== 活动情况 ==========
// ========== 正常情况 ==========
price.setTotalPrice(price.getTotalPrice() + item.getPrice() * item.getCount());
price.setDiscountPrice(price.getDiscountPrice() + item.getDiscountPrice());
price.setDeliveryPrice(price.getDeliveryPrice() + item.getDeliveryPrice());

View File

@ -0,0 +1,45 @@
package cn.iocoder.yudao.module.trade.service.price.calculator;
import cn.iocoder.yudao.module.promotion.api.seckill.SeckillActivityApi;
import cn.iocoder.yudao.module.promotion.api.seckill.dto.SeckillActivityProductRespDTO;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
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.convertSet;
/**
* 秒杀活动的 {@link TradePriceCalculator} 实现类
*
* @author HUIHUI
*/
@Component
@Order(TradePriceCalculator.ORDER_DISCOUNT_ACTIVITY)
public class TradeSeckillActivityPriceCalculator implements TradePriceCalculator {
@Resource
private SeckillActivityApi activityApi;
@Override
public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
// 1判断订单类型和是否具有秒杀活动编号
if (param.getSeckillActivityId() == null) {
return;
}
// 2获取秒杀活动商品信息
List<SeckillActivityProductRespDTO> productList = activityApi.getSeckillActivityProductList(param.getSeckillActivityId(), convertSet(param.getItems(),
TradePriceCalculateReqBO.Item::getSkuId));
Map<Long, SeckillActivityProductRespDTO> productMap = convertMap(productList, SeckillActivityProductRespDTO::getSkuId);
result.getItems().forEach(item -> {
SeckillActivityProductRespDTO product = productMap.get(item.getSkuId());
item.setActivityPrice(product.getSeckillPrice()); // 设置活动金额
});
}
}