mirror of
https://gitee.com/hhyykk/ipms-sjy.git
synced 2025-08-12 17:21:52 +08:00
mall + promotion:review 秒杀、评论代码
This commit is contained in:
@@ -20,6 +20,7 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
|
||||
@Data
|
||||
public class SeckillActivityBaseVO {
|
||||
|
||||
// TODO @puhui999:对应单 spuId 哈
|
||||
@Schema(description = "秒杀活动商品id", requiredMode = Schema.RequiredMode.REQUIRED, example = "[121,1212]")
|
||||
@NotNull(message = "秒杀活动商品不能为空")
|
||||
private List<Long> spuIds;
|
||||
@@ -45,7 +46,7 @@ public class SeckillActivityBaseVO {
|
||||
@NotNull(message = "排序不能为空")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "秒杀时段id", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1,2,3]")
|
||||
@Schema(description = "秒杀时段 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1,2,3]")
|
||||
@NotNull(message = "秒杀时段不能为空")
|
||||
private List<Long> configIds;
|
||||
|
||||
@@ -55,6 +56,7 @@ public class SeckillActivityBaseVO {
|
||||
@Schema(description = "单次限够数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "31683")
|
||||
private Integer singleLimitCount;
|
||||
|
||||
// TODO @puhui999:这个应该是计算出来的字段,只返回,create 和 update 不用哈
|
||||
@Schema(description = "秒杀总库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
private Integer totalStock;
|
||||
|
||||
|
@@ -9,11 +9,6 @@ import lombok.ToString;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 管理后台 - 秒杀活动创建 Request VO
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
@Schema(description = "管理后台 - 秒杀活动创建 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
|
@@ -7,11 +7,6 @@ import lombok.ToString;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 管理后台 - 秒杀活动的详细 Response VO
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
@Schema(description = "管理后台 - 秒杀活动的详细 Response VO")
|
||||
@Data
|
||||
@ToString(callSuper = true)
|
||||
|
@@ -28,5 +28,4 @@ public class SeckillActivityRespVO extends SeckillActivityBaseVO {
|
||||
@Schema(description = "活动状态 开启:0 禁用:1", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
private Integer status;
|
||||
|
||||
|
||||
}
|
||||
|
@@ -1,22 +1,13 @@
|
||||
package cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity;
|
||||
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.product.SeckillProductRespVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.product.SeckillProductUpdateReqVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 管理后台 - 秒杀活动更新 Request VO
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
@Schema(description = "管理后台 - 秒杀活动更新 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@@ -29,5 +20,4 @@ public class SeckillActivityUpdateReqVO extends SeckillActivityBaseVO {
|
||||
@Schema(description = "秒杀商品", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private List<SeckillProductUpdateReqVO> products;
|
||||
|
||||
|
||||
}
|
||||
|
@@ -5,11 +5,6 @@ import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* 管理后台 秒杀时段创建 Request VO
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
@Schema(description = "管理后台 - 秒杀时段创建 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
|
@@ -6,6 +6,7 @@ import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
// TODO @puhui:VO 上不写注释,已经有注解啦。
|
||||
/**
|
||||
* 管理后台 - 秒杀时段分页 Request VO
|
||||
*
|
||||
|
@@ -4,17 +4,9 @@ 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;
|
||||
|
||||
/**
|
||||
* 管理后台 - 秒杀时段 Response VO
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
@Schema(description = "管理后台 - 秒杀时段 Response VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@@ -28,7 +20,6 @@ public class SeckillConfigRespVO extends SeckillConfigBaseVO {
|
||||
private Integer seckillActivityCount;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
|
@@ -7,11 +7,6 @@ import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 管理后台 - 秒杀时段配置精简信息 Response VO
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
@Schema(description = "管理后台 - 秒杀时段配置精简信息 Response VO")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
|
@@ -7,11 +7,6 @@ import lombok.ToString;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 管理后台 - 秒杀时段更新 Request VO
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
@Schema(description = "管理后台 - 秒杀时段更新 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
|
@@ -7,11 +7,6 @@ import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 管理后台 - 修改时段配置状态 Request VO
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
@Schema(description = "管理后台 - 修改时段配置状态 Request VO")
|
||||
@Data
|
||||
public class SeckillConfigUpdateStatusReqVo {
|
||||
@@ -20,7 +15,7 @@ public class SeckillConfigUpdateStatusReqVo {
|
||||
@NotNull(message = "时段配置编号不能为空")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "状态不能为空")
|
||||
@InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}")
|
||||
private Integer status;
|
||||
|
@@ -14,6 +14,7 @@ import javax.validation.constraints.NotNull;
|
||||
@Data
|
||||
public class SeckillProductBaseVO {
|
||||
|
||||
// TODO @puhui:spuId 不用传递;因为一个秒杀活动只对应一个 SPU 哈;
|
||||
@Schema(description = "商品spu_id", requiredMode = Schema.RequiredMode.REQUIRED, example = "30563")
|
||||
@NotNull(message = "商品spu_id不能为空")
|
||||
private Long spuId;
|
||||
|
@@ -59,6 +59,7 @@ public class AppActivityController {
|
||||
}
|
||||
Map<Long, List<AppActivityRespVO>> map = new HashMap<>();
|
||||
map.put(109L, randomList);
|
||||
map.put(2L, randomList);
|
||||
return success(map);
|
||||
}
|
||||
|
||||
|
@@ -69,7 +69,6 @@ public interface SeckillActivityConvert {
|
||||
&& ObjectUtil.equals(productDO.getSeckillPrice(), productVO.getSeckillPrice());
|
||||
//&& ObjectUtil.equals(productDO.getQuota(), productVO.getQuota())
|
||||
//&& ObjectUtil.equals(productDO.getLimitCount(), productVO.getLimitCount());
|
||||
|
||||
}
|
||||
|
||||
default List<SeckillProductDO> convertList(SeckillActivityDO seckillActivity, List<SeckillProductCreateReqVO> products) {
|
||||
|
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillconfig.SeckillConfigDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
@@ -34,10 +35,14 @@ public class SeckillProductDO extends BaseDO {
|
||||
private Long id;
|
||||
/**
|
||||
* 秒杀活动 id
|
||||
*
|
||||
* 关联 {@link SeckillActivityDO#getId()}
|
||||
*/
|
||||
private Long activityId;
|
||||
/**
|
||||
* 秒杀时段 id
|
||||
*
|
||||
* 关联 {@link SeckillConfigDO#getId()}
|
||||
*/
|
||||
@TableField(typeHandler = LongListTypeHandler.class)
|
||||
private List<Long> configIds;
|
||||
@@ -57,8 +62,10 @@ public class SeckillProductDO extends BaseDO {
|
||||
* 秒杀库存
|
||||
*/
|
||||
private Integer stock;
|
||||
|
||||
/**
|
||||
* 秒杀商品状态
|
||||
*
|
||||
* 枚举 {@link CommonStatusEnum 对应的类}
|
||||
*/
|
||||
private Integer activityStatus;
|
||||
|
@@ -38,13 +38,14 @@ public class SeckillConfigDO extends BaseDO {
|
||||
* 结束时间点
|
||||
*/
|
||||
private String endTime;
|
||||
// TODO puhui999:应该是轮播图; private List<String> sliderPicUrls;
|
||||
/**
|
||||
* 秒杀主图
|
||||
*/
|
||||
private String picUrl;
|
||||
/**
|
||||
* 状态 开启:0 禁用:1
|
||||
* <p>
|
||||
* 状态
|
||||
*
|
||||
* 枚举 {@link CommonStatusEnum 对应的类}
|
||||
*/
|
||||
private Integer status;
|
||||
|
@@ -17,6 +17,7 @@ import java.util.List;
|
||||
*/
|
||||
@Mapper
|
||||
public interface SeckillActivityMapper extends BaseMapperX<SeckillActivityDO> {
|
||||
|
||||
default PageResult<SeckillActivityDO> selectPage(SeckillActivityPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<SeckillActivityDO>()
|
||||
.likeIfPresent(SeckillActivityDO::getName, reqVO.getName())
|
||||
@@ -30,4 +31,5 @@ public interface SeckillActivityMapper extends BaseMapperX<SeckillActivityDO> {
|
||||
return selectList(new LambdaQueryWrapperX<SeckillActivityDO>()
|
||||
.eqIfPresent(SeckillActivityDO::getStatus, status));
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -65,22 +65,24 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
|
||||
validateProductSkuExistence(createReqVO.getSpuIds(), createReqVO.getProducts());
|
||||
|
||||
// 插入秒杀活动
|
||||
SeckillActivityDO seckillActivity = SeckillActivityConvert.INSTANCE.convert(createReqVO)
|
||||
SeckillActivityDO activity = SeckillActivityConvert.INSTANCE.convert(createReqVO)
|
||||
.setStatus(PromotionUtils.calculateActivityStatus(createReqVO.getEndTime()));
|
||||
seckillActivityMapper.insert(seckillActivity);
|
||||
seckillActivityMapper.insert(activity);
|
||||
// 插入商品
|
||||
List<SeckillProductDO> productDOs = SeckillActivityConvert.INSTANCE.convertList(seckillActivity, createReqVO.getProducts());
|
||||
seckillProductMapper.insertBatch(productDOs);
|
||||
return seckillActivity.getId();
|
||||
List<SeckillProductDO> product = SeckillActivityConvert.INSTANCE.convertList(activity, createReqVO.getProducts());
|
||||
seckillProductMapper.insertBatch(product);
|
||||
return activity.getId();
|
||||
}
|
||||
|
||||
private <T extends SeckillProductBaseVO> void validateProductSkuExistence(List<Long> spuIds, List<T> products) {
|
||||
Set<Long> convertedSpuIds = CollectionUtils.convertSet(products, T::getSpuId);
|
||||
// 校验 spu 个数是否相等
|
||||
// TODO @puhui999:不用校验 SPU 哈,只校验 sku 对应的 spuId 是否一致;
|
||||
Set<Long> convertedSpuIds = CollectionUtils.convertSet(products, T::getSpuId);
|
||||
if (ObjectUtil.notEqual(spuIds.size(), convertedSpuIds.size())) {
|
||||
throw exception(SKU_NOT_EXISTS);
|
||||
}
|
||||
// 获取所选 spu下的所有 sku
|
||||
// TODO @puhui999:变量可以简单一点;skus
|
||||
List<ProductSkuRespDTO> skuRespDTOs = productSkuApi.getSkuListBySpuId(spuIds);
|
||||
// 校验 sku 个数是否一致
|
||||
Set<Long> skuIdsSet = CollectionUtils.convertSet(products, T::getSkuId);
|
||||
@@ -153,6 +155,7 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
|
||||
* @param updateReqVO 更新的请求VO
|
||||
*/
|
||||
private void updateSeckillProduct(SeckillActivityUpdateReqVO updateReqVO) {
|
||||
// TODO puhui999:要不这里简单一点;删除原本的,插入新增的;不做的这么细致
|
||||
// TODO puhui999:后续完善
|
||||
//List<SeckillProductDO> seckillProductDOs = seckillProductMapper.selectListByActivityId(updateReqVO.getId());
|
||||
//List<SeckillProductUpdateReqVO> products = updateReqVO.getProducts();
|
||||
|
@@ -82,6 +82,7 @@ public class SeckillConfigServiceImpl implements SeckillConfigService {
|
||||
private void validateSeckillConfigConflict(String startTime, String endTime) {
|
||||
LocalTime startTime1 = LocalTime.parse(startTime);
|
||||
LocalTime endTime1 = LocalTime.parse(endTime);
|
||||
// TODO @puhui999: 这个可以用 validator 里的 assertTrue 去做哈;
|
||||
// 检查选择的时间是否相等
|
||||
if (startTime1.equals(endTime1)) {
|
||||
throw exception(SECKILL_TIME_EQUAL);
|
||||
@@ -93,11 +94,13 @@ public class SeckillConfigServiceImpl implements SeckillConfigService {
|
||||
// 查询出所有的时段配置
|
||||
List<SeckillConfigDO> configDOs = seckillConfigMapper.selectList();
|
||||
// 过滤出重叠的时段 ids
|
||||
// TODO @puhui999:感觉 findOne 就可以了?
|
||||
Set<Long> ids = configDOs.stream().filter((config) -> {
|
||||
LocalTime startTime2 = LocalTime.parse(config.getStartTime());
|
||||
LocalTime endTime2 = LocalTime.parse(config.getEndTime());
|
||||
// 判断时间是否重叠
|
||||
// 开始时间在已配置时段的结束时间之前 且 结束时间在已配置时段的开始时间之后 []
|
||||
// todo @puhui999:LocalDateUtils 可以写个工具类?是否是有重叠的时间?感觉别的场景,可能也会有需要
|
||||
return startTime1.isBefore(endTime2) && endTime1.isAfter(startTime2)
|
||||
// 开始时间在已配置时段的开始时间之前 且 结束时间在已配置时段的开始时间之后 (] 或 ()
|
||||
|| startTime1.isBefore(startTime2) && endTime1.isAfter(startTime2)
|
||||
@@ -127,6 +130,7 @@ public class SeckillConfigServiceImpl implements SeckillConfigService {
|
||||
if (seckillConfigMapper.selectBatchIds(configIds).size() != configIds.size()) {
|
||||
throw exception(SECKILL_TIME_NOT_EXISTS);
|
||||
}
|
||||
// TODO @puhui999:应该要校验个 status 哈;如果有禁用的,也不行
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -134,11 +138,13 @@ public class SeckillConfigServiceImpl implements SeckillConfigService {
|
||||
return seckillConfigMapper.selectPage(pageVO);
|
||||
}
|
||||
|
||||
// TODO @puhui999:写个查询状态的; 尽可能通用哈
|
||||
@Override
|
||||
public List<SeckillConfigDO> getListAllSimple() {
|
||||
return seckillConfigMapper.selectList(SeckillConfigDO::getStatus, CommonStatusEnum.ENABLE.getStatus());
|
||||
}
|
||||
|
||||
// TODO @puhui999: 这个要不合并到更新操作里? 不单独有个操作咧;
|
||||
@Override
|
||||
public void updateSeckillConfigStatus(Long id, Integer status) {
|
||||
// 校验秒杀时段是否存在
|
||||
|
@@ -29,7 +29,7 @@ import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Import(SeckillConfigServiceImpl.class)
|
||||
@Disabled // TODO 芋艿:未来开启
|
||||
@Disabled // TODO 芋艿:未来开启;后续要 review 下
|
||||
public class SeckillConfigServiceImplTest extends BaseDbUnitTest {
|
||||
|
||||
@Resource
|
||||
|
Reference in New Issue
Block a user