初步完成商品CRUD

This commit is contained in:
puhui999 2023-05-02 02:33:14 +08:00
parent 6428f0fcaf
commit 70816371e5
16 changed files with 133 additions and 184 deletions

View File

@ -50,4 +50,14 @@ public class ProductSkuBaseVO {
@Schema(description = "商品体积", example = "1024") // 单位m^3 平米 @Schema(description = "商品体积", example = "1024") // 单位m^3 平米
private Double volume; private Double volume;
/**
* 一级分销的佣金单位
*/
@Schema(description = "一级分销的佣金", example = "1024")
private Integer subCommissionFirstPrice;
/**
* 二级分销的佣金单位
*/
@Schema(description = "二级分销的佣金", example = "1024")
private Integer subCommissionSecondPrice;
} }

View File

@ -94,7 +94,7 @@ public class ProductSpuController {
@GetMapping("/page") @GetMapping("/page")
@Operation(summary = "获得商品 SPU 分页") @Operation(summary = "获得商品 SPU 分页")
@PreAuthorize("@ss.hasPermission('product:spu:query')") @PreAuthorize("@ss.hasPermission('product:spu:query')")
public CommonResult<PageResult<ProductSpuRespVO>> getSpuPage(@Valid ProductSpuPageReqVO pageVO) { public CommonResult<PageResult<ProductSpuPageRespVO>> getSpuPage(@Valid ProductSpuPageReqVO pageVO) {
return success(ProductSpuConvert.INSTANCE.convertPage(productSpuService.getSpuPage(pageVO))); return success(ProductSpuConvert.INSTANCE.convertPage(productSpuService.getSpuPage(pageVO)));
} }

View File

@ -38,16 +38,11 @@ public class ProductSpuBaseVO {
@NotEmpty(message = "商品详情不能为空") @NotEmpty(message = "商品详情不能为空")
private String description; private String description;
@Schema(description = "商品条码(一维码)", required = true, example = "芋道")
@NotEmpty(message = "商品条码(一维码)不能为空")
private String barCode;
@Schema(description = "商品分类编号", required = true, example = "芋道") @Schema(description = "商品分类编号", required = true, example = "芋道")
@NotEmpty(message = "商品分类编号不能为空") @NotNull(message = "商品分类编号不能为空")
private Long categoryId; private Long categoryId;
@Schema(description = "商品品牌编号", required = true, example = "芋道") @Schema(description = "商品品牌编号", required = true, example = "芋道")
@NotEmpty(message = "商品品牌编号不能为空")
private Long brandId; private Long brandId;
@Schema(description = "商品封面图", required = true, example = "芋道") @Schema(description = "商品封面图", required = true, example = "芋道")
@ -55,99 +50,71 @@ public class ProductSpuBaseVO {
private String picUrl; private String picUrl;
@Schema(description = "商品轮播图", required = true) @Schema(description = "商品轮播图", required = true)
@NotEmpty(message = "商品轮播图不能为空")
private List<String> sliderPicUrls; private List<String> sliderPicUrls;
@Schema(description = "商品视频") @Schema(description = "商品视频")
private String videoUrl; private String videoUrl;
@Schema(description = "单位", required = true, example = "1") @Schema(description = "单位", required = true, example = "1")
@NotEmpty(message = "商品单位不能为空") @NotNull(message = "商品单位不能为空")
private Integer unit; private Integer unit;
@Schema(description = "排序字段", required = true, example = "1") @Schema(description = "排序字段", required = true, example = "1")
@NotEmpty(message = "商品排序字段不能为空") @NotNull(message = "商品排序字段不能为空")
private Integer sort; private Integer sort;
@Schema(description = "商品状态", required = true, example = "1")
@NotEmpty(message = "商品状态不能为空")
private Integer status;
// ========== SKU 相关字段 ========= // ========== SKU 相关字段 =========
@Schema(description = "规格类型", required = true, example = "true") @Schema(description = "规格类型", required = true, example = "true")
@NotEmpty(message = "商品规格类型不能为空") @NotNull(message = "商品规格类型不能为空")
private Boolean specType; private Boolean specType;
@Schema(description = "商品价格", required = true, example = "1212")
@NotEmpty(message = "商品价格不能为空")
private Integer price;
@Schema(description = "市场价", required = true, example = "3")
@NotEmpty(message = "商品市场价不能为空")
private Integer marketPrice;
@Schema(description = "成本价", required = true, example = "12")
@NotEmpty(message = "商品成本价不能为空")
private Integer costPrice;
@Schema(description = "库存", required = true, example = "111")
@NotEmpty(message = "商品库存不能为空")
private Integer stock;
// ========== 物流相关字段 ========= // ========== 物流相关字段 =========
@Schema(description = "物流配置模板编号", required = true, example = "111") @Schema(description = "物流配置模板编号", required = true, example = "111")
@NotEmpty(message = "物流配置模板编号不能为空") @NotNull(message = "物流配置模板编号不能为空")
private Long deliveryTemplateId; private Long deliveryTemplateId;
// ========== 营销相关字段 ========= // ========== 营销相关字段 =========
@Schema(description = "是否热卖推荐", required = true, example = "true") @Schema(description = "是否热卖推荐", required = true, example = "true")
@NotEmpty(message = "商品推荐不能为空") @NotNull(message = "商品推荐不能为空")
private Boolean recommendHot; private Boolean recommendHot;
@Schema(description = "是否优惠推荐", required = true, example = "true") @Schema(description = "是否优惠推荐", required = true, example = "true")
@NotEmpty(message = "商品推荐不能为空") @NotNull(message = "商品推荐不能为空")
private Boolean recommendBenefit; private Boolean recommendBenefit;
@Schema(description = "是否精品推荐", required = true, example = "true") @Schema(description = "是否精品推荐", required = true, example = "true")
@NotEmpty(message = "商品推荐不能为空") @NotNull(message = "商品推荐不能为空")
private Boolean recommendBest; private Boolean recommendBest;
@Schema(description = "是否新品推荐", required = true, example = "true") @Schema(description = "是否新品推荐", required = true, example = "true")
@NotEmpty(message = "商品推荐不能为空") @NotNull(message = "商品推荐不能为空")
private Boolean recommendNew; private Boolean recommendNew;
@Schema(description = "是否优品推荐", required = true, example = "true") @Schema(description = "是否优品推荐", required = true, example = "true")
@NotEmpty(message = "商品推荐不能为空") @NotNull(message = "商品推荐不能为空")
private Boolean recommendGood; private Boolean recommendGood;
@Schema(description = "赠送积分", required = true, example = "111") @Schema(description = "赠送积分", required = true, example = "111")
@NotEmpty(message = "商品赠送积分不能为空") @NotNull(message = "商品赠送积分不能为空")
private Integer giveIntegral; private Integer giveIntegral;
@Schema(description = "赠送的优惠劵编号的数组") @Schema(description = "赠送的优惠劵编号的数组") // TODO 这块前端还未实现
private List<Long> giveCouponTemplateIds; private List<Long> giveCouponTemplateIds;
@Schema(description = "分销类型") @Schema(description = "分销类型")
@NotNull(message = "商品分销类型不能为空")
private Boolean subCommissionType; private Boolean subCommissionType;
@Schema(description = "活动展示顺序") @Schema(description = "活动展示顺序") // TODO 这块前端还未实现
private List<Integer> activityOrders; private List<Integer> activityOrders;
// ========== 统计相关字段 ========= // ========== 统计相关字段 =========
@Schema(description = "商品销量", required = true, example = "芋道")
@NotEmpty(message = "商品销量不能为空")
private Integer salesCount;
@Schema(description = "虚拟销量", required = true, example = "芋道") @Schema(description = "虚拟销量", required = true, example = "芋道")
@NotEmpty(message = "商品虚拟销量不能为空")
private Integer virtualSalesCount; private Integer virtualSalesCount;
@Schema(description = "浏览量", required = true, example = "芋道")
@NotEmpty(message = "商品浏览量不能为空")
private Integer browseCount;
} }

View File

@ -15,31 +15,7 @@ public class ProductSpuPageReqVO extends PageParam {
@Schema(description = "商品名称", example = "yutou") @Schema(description = "商品名称", example = "yutou")
private String name; private String name;
@Schema(description = "商品编码", example = "yudaoyuanma")
private String code;
@Schema(description = "分类编号", example = "1") @Schema(description = "分类编号", example = "1")
private Long categoryId; private Long categoryId;
@Schema(description = "商品品牌编号", example = "1")
private Long brandId;
@Schema(description = "上下架状态", example = "1")
private Integer status;
@Schema(description = "销量最小值", example = "1")
private Integer salesCountMin;
@Schema(description = "销量最大值", example = "1024")
private Integer salesCountMax;
@Schema(description = "市场价最小值", example = "1")
private Integer marketPriceMin;
@Schema(description = "市场价最大值", example = "1024")
private Integer marketPriceMax;
@Schema(description = "是否库存告警", example = "true")
private Boolean alarmStock;
} }

View File

@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.product.controller.admin.spu.vo;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 商品 SPU 分页 response VO")
@Data
public class ProductSpuPageRespVO {
@Schema(description = "spuId", example = "1")
private Long id;
@Schema(description = "商品封面图", example = "1")
private String picUrl;
@Schema(description = "商品名称", example = "1")
private String name;
@Schema(description = "商品价格", example = "1")
private Integer price;
@Schema(description = "商品销量", example = "1")
private Integer salesCount;
@Schema(description = "商品排序", example = "1")
private Integer stock;
@Schema(description = "商品封面图", example = "1")
private Integer sort;
@Schema(description = "商品创建时间", example = "1")
private LocalDateTime createTime;
@Schema(description = "商品状态", example = "1")
private Integer status;
}

View File

@ -19,22 +19,4 @@ public class ProductSpuRespVO extends ProductSpuBaseVO {
@Schema(description = "创建时间") @Schema(description = "创建时间")
private LocalDateTime createTime; private LocalDateTime createTime;
// ========== SKU 相关字段 =========
@Schema(description = "库存", required = true, example = "true")
private Integer totalStock;
@Schema(description = " 最小价格,单位使用:分", required = true, example = "1024")
private Integer minPrice;
@Schema(description = "最大价格,单位使用:分", required = true, example = "1024")
private Integer maxPrice;
@Schema(description = "商品销量", example = "1024")
private Integer salesCount;
// ========== 统计相关字段 =========
@Schema(description = "点击量", example = "1024")
private Integer clickCount;
} }

View File

@ -35,9 +35,8 @@ public interface ProductSkuConvert {
List<ProductSkuDO> convertList06(List<ProductSkuCreateOrUpdateReqVO> list); List<ProductSkuDO> convertList06(List<ProductSkuCreateOrUpdateReqVO> list);
default List<ProductSkuDO> convertList06(List<ProductSkuCreateOrUpdateReqVO> list, Long spuId, String spuName) { default List<ProductSkuDO> convertList06(List<ProductSkuCreateOrUpdateReqVO> list, Long spuId) {
List<ProductSkuDO> result = convertList06(list); List<ProductSkuDO> result = convertList06(list);
// result.forEach(item -> item.setSpuId(spuId).setSpuName(spuName)); TODO ProductSkuDO中已经没有name相关属性
result.forEach(item -> item.setSpuId(spuId)); result.forEach(item -> item.setSpuId(spuId));
return result; return result;
} }

View File

@ -38,7 +38,7 @@ public interface ProductSpuConvert {
List<ProductSpuDO> convertList(List<ProductSpuDO> list); List<ProductSpuDO> convertList(List<ProductSpuDO> list);
PageResult<ProductSpuRespVO> convertPage(PageResult<ProductSpuDO> page); PageResult<ProductSpuPageRespVO> convertPage(PageResult<ProductSpuDO> page);
ProductSpuPageReqVO convert(AppProductSpuPageReqVO bean); ProductSpuPageReqVO convert(AppProductSpuPageReqVO bean);

View File

@ -116,19 +116,19 @@ public class ProductSpuDO extends TenantBaseDO {
/** /**
* 商品价格单位使用 * 商品价格单位使用
* *
* 基于其对应的 {@link ProductSkuDO#getPrice()} 最小值 * 基于其对应的 {@link ProductSkuDO#getPrice()} sku单价最低的商品的
*/ */
private Integer price; private Integer price;
/** /**
* 市场价单位使用 * 市场价单位使用
* *
* 基于其对应的 {@link ProductSkuDO#getMarketPrice()} 最大值 TODO 芋艿待确定最大还是最小 * 基于其对应的 {@link ProductSkuDO#getMarketPrice()} sku单价最低的商品的
*/ */
private Integer marketPrice; private Integer marketPrice;
/** /**
* 成本价单位使用 * 成本价单位使用
* *
* 基于其对应的 {@link ProductSkuDO#getCostPrice()} 最大值 TODO 芋艿待确定最大还是最小 * 基于其对应的 {@link ProductSkuDO#getCostPrice()} sku单价最低的商品的
*/ */
private Integer costPrice; private Integer costPrice;
/** /**

View File

@ -24,11 +24,6 @@ public interface ProductSpuMapper extends BaseMapperX<ProductSpuDO> {
return selectPage(reqVO, new LambdaQueryWrapperX<ProductSpuDO>() return selectPage(reqVO, new LambdaQueryWrapperX<ProductSpuDO>()
.likeIfPresent(ProductSpuDO::getName, reqVO.getName()) .likeIfPresent(ProductSpuDO::getName, reqVO.getName())
.eqIfPresent(ProductSpuDO::getCategoryId, reqVO.getCategoryId()) .eqIfPresent(ProductSpuDO::getCategoryId, reqVO.getCategoryId())
.eqIfPresent(ProductSpuDO::getStatus, reqVO.getStatus())
.leIfPresent(ProductSpuDO::getSalesCount, reqVO.getSalesCountMax())
.geIfPresent(ProductSpuDO::getSalesCount, reqVO.getSalesCountMin())
.leIfPresent(ProductSpuDO::getMarketPrice, reqVO.getMarketPriceMax())
.geIfPresent(ProductSpuDO::getMarketPrice, reqVO.getMarketPriceMin())
.orderByDesc(ProductSpuDO::getSort)); .orderByDesc(ProductSpuDO::getSort));
} }
@ -36,13 +31,7 @@ public interface ProductSpuMapper extends BaseMapperX<ProductSpuDO> {
return selectPage(reqVO, new LambdaQueryWrapperX<ProductSpuDO>() return selectPage(reqVO, new LambdaQueryWrapperX<ProductSpuDO>()
.likeIfPresent(ProductSpuDO::getName, reqVO.getName()) .likeIfPresent(ProductSpuDO::getName, reqVO.getName())
.eqIfPresent(ProductSpuDO::getCategoryId, reqVO.getCategoryId()) .eqIfPresent(ProductSpuDO::getCategoryId, reqVO.getCategoryId())
.eqIfPresent(ProductSpuDO::getStatus, reqVO.getStatus())
.leIfPresent(ProductSpuDO::getSalesCount, reqVO.getSalesCountMax())
.geIfPresent(ProductSpuDO::getSalesCount, reqVO.getSalesCountMin())
.leIfPresent(ProductSpuDO::getMarketPrice, reqVO.getMarketPriceMax())
.geIfPresent(ProductSpuDO::getMarketPrice, reqVO.getMarketPriceMin())
.inIfPresent(ProductSpuDO::getId, alarmStockSpuIds) // 库存告警 .inIfPresent(ProductSpuDO::getId, alarmStockSpuIds) // 库存告警
.eqIfPresent(ProductSpuDO::getStatus, reqVO.getStatus())
.orderByDesc(ProductSpuDO::getSort)); .orderByDesc(ProductSpuDO::getSort));
} }
@ -52,8 +41,6 @@ public interface ProductSpuMapper extends BaseMapperX<ProductSpuDO> {
.eqIfPresent(ProductSpuDO::getStatus, status); .eqIfPresent(ProductSpuDO::getStatus, status);
// 排序逻辑 // 排序逻辑
if (Objects.equals(pageReqVO.getSortField(), AppProductSpuPageReqVO.SORT_FIELD_PRICE)) { if (Objects.equals(pageReqVO.getSortField(), AppProductSpuPageReqVO.SORT_FIELD_PRICE)) {
// TODO ProductSpuDO 已经没有maxPrice 属性
//query.orderBy(true, pageReqVO.getSortAsc(), ProductSpuDO::getMaxPrice);
} else if (Objects.equals(pageReqVO.getSortField(), AppProductSpuPageReqVO.SORT_FIELD_SALES_COUNT)) { } else if (Objects.equals(pageReqVO.getSortField(), AppProductSpuPageReqVO.SORT_FIELD_SALES_COUNT)) {
query.orderBy(true, pageReqVO.getSortAsc(), ProductSpuDO::getSalesCount); query.orderBy(true, pageReqVO.getSortAsc(), ProductSpuDO::getSalesCount);
} }

View File

@ -100,7 +100,7 @@ public class ProductCategoryServiceImpl implements ProductCategoryService {
if (category == null) { if (category == null) {
throw exception(CATEGORY_NOT_EXISTS); throw exception(CATEGORY_NOT_EXISTS);
} }
if (Objects.equals(category.getStatus(), CommonStatusEnum.ENABLE.getStatus())) { if (Objects.equals(category.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {
throw exception(CATEGORY_DISABLED, category.getName()); throw exception(CATEGORY_DISABLED, category.getName());
} }
} }

View File

@ -56,19 +56,17 @@ public interface ProductSkuService {
* 批量创建 SKU * 批量创建 SKU
* *
* @param spuId 商品 SPU 编号 * @param spuId 商品 SPU 编号
* @param spuName 商品 SPU 名称
* @param list SKU 对象集合 * @param list SKU 对象集合
*/ */
void createSkuList(Long spuId, String spuName, List<ProductSkuCreateOrUpdateReqVO> list); void createSkuList(Long spuId, List<ProductSkuCreateOrUpdateReqVO> list);
/** /**
* 根据 SPU 编号批量更新它的 SKU 信息 * 根据 SPU 编号批量更新它的 SKU 信息
* *
* @param spuId SPU 编码 * @param spuId SPU 编码
* @param spuName 商品 SPU 名称
* @param skus SKU 的集合 * @param skus SKU 的集合
*/ */
void updateSkuList(Long spuId, String spuName, List<ProductSkuCreateOrUpdateReqVO> skus); void updateSkuList(Long spuId, List<ProductSkuCreateOrUpdateReqVO> skus);
/** /**
* 更新 SKU 库存增量 * 更新 SKU 库存增量

View File

@ -84,10 +84,17 @@ public class ProductSkuServiceImpl implements ProductSkuService {
return; return;
} }
// 0校验skus是否为空
if (CollUtil.isEmpty(skus)){
throw exception(SKU_NOT_EXISTS);
}
// 1校验属性项存在 // 1校验属性项存在
Set<Long> propertyIds = skus.stream().filter(p -> p.getProperties() != null) Set<Long> propertyIds = skus.stream().filter(p -> p.getProperties() != null)
.flatMap(p -> p.getProperties().stream()) // 遍历多个 Property 属性 // 遍历多个 Property 属性
.map(ProductSkuCreateOrUpdateReqVO.Property::getPropertyId) // 将每个 Property 转换成对应的 propertyId最后形成集合 .flatMap(p -> p.getProperties().stream())
// 将每个 Property 转换成对应的 propertyId最后形成集合
.map(ProductSkuCreateOrUpdateReqVO.Property::getPropertyId)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
List<ProductPropertyDO> propertyList = productPropertyService.getPropertyList(propertyIds); List<ProductPropertyDO> propertyList = productPropertyService.getPropertyList(propertyIds);
if (propertyList.size() != propertyIds.size()) { if (propertyList.size() != propertyIds.size()) {
@ -107,22 +114,24 @@ public class ProductSkuServiceImpl implements ProductSkuService {
int attrValueIdsSize = skus.get(0).getProperties().size(); int attrValueIdsSize = skus.get(0).getProperties().size();
for (int i = 1; i < skus.size(); i++) { for (int i = 1; i < skus.size(); i++) {
if (attrValueIdsSize != skus.get(i).getProperties().size()) { if (attrValueIdsSize != skus.get(i).getProperties().size()) {
throw exception(ErrorCodeConstants.SPU_ATTR_NUMBERS_MUST_BE_EQUALS); throw exception(SPU_ATTR_NUMBERS_MUST_BE_EQUALS);
} }
} }
// 4. 最后校验每个 Sku 之间不是重复的 // 4. 最后校验每个 Sku 之间不是重复的
Set<Set<Long>> skuAttrValues = new HashSet<>(); // 每个元素都是一个 Sku attrValueId 集合这样通过最外层的 Set 判断是否有重复的. // 每个元素都是一个 Sku attrValueId 集合这样通过最外层的 Set 判断是否有重复的.
Set<Set<Long>> skuAttrValues = new HashSet<>();
for (ProductSkuCreateOrUpdateReqVO sku : skus) { for (ProductSkuCreateOrUpdateReqVO sku : skus) {
if (!skuAttrValues.add(convertSet(sku.getProperties(), ProductSkuCreateOrUpdateReqVO.Property::getValueId))) { // 添加失败说明重复 // 添加失败说明重复
throw exception(ErrorCodeConstants.SPU_SKU_NOT_DUPLICATE); if (!skuAttrValues.add(convertSet(sku.getProperties(), ProductSkuCreateOrUpdateReqVO.Property::getValueId))) {
throw exception(SPU_SKU_NOT_DUPLICATE);
} }
} }
} }
@Override @Override
public void createSkuList(Long spuId, String spuName, List<ProductSkuCreateOrUpdateReqVO> skuCreateReqList) { public void createSkuList(Long spuId, List<ProductSkuCreateOrUpdateReqVO> skuCreateReqList) {
productSkuMapper.insertBatch(ProductSkuConvert.INSTANCE.convertList06(skuCreateReqList, spuId, spuName)); productSkuMapper.insertBatch(ProductSkuConvert.INSTANCE.convertList06(skuCreateReqList, spuId));
} }
@Override @Override
@ -152,7 +161,7 @@ public class ProductSkuServiceImpl implements ProductSkuService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void updateSkuList(Long spuId, String spuName, List<ProductSkuCreateOrUpdateReqVO> skus) { public void updateSkuList(Long spuId, List<ProductSkuCreateOrUpdateReqVO> skus) {
// 构建属性与 SKU 的映射关系; // 构建属性与 SKU 的映射关系;
Map<String, Long> existsSkuMap = convertMap(productSkuMapper.selectListBySpuId(spuId), Map<String, Long> existsSkuMap = convertMap(productSkuMapper.selectListBySpuId(spuId),
ProductSkuConvert.INSTANCE::buildPropertyKey, ProductSkuDO::getId); ProductSkuConvert.INSTANCE::buildPropertyKey, ProductSkuDO::getId);
@ -160,13 +169,14 @@ public class ProductSkuServiceImpl implements ProductSkuService {
// 拆分三个集合新插入的需要更新的需要删除的 // 拆分三个集合新插入的需要更新的需要删除的
List<ProductSkuDO> insertSkus = new ArrayList<>(); List<ProductSkuDO> insertSkus = new ArrayList<>();
List<ProductSkuDO> updateSkus = new ArrayList<>(); List<ProductSkuDO> updateSkus = new ArrayList<>();
List<ProductSkuDO> allUpdateSkus = ProductSkuConvert.INSTANCE.convertList06(skus, null, spuName); List<ProductSkuDO> allUpdateSkus = ProductSkuConvert.INSTANCE.convertList06(skus, null);
allUpdateSkus.forEach(sku -> { allUpdateSkus.forEach(sku -> {
String propertiesKey = ProductSkuConvert.INSTANCE.buildPropertyKey(sku); String propertiesKey = ProductSkuConvert.INSTANCE.buildPropertyKey(sku);
// 1找得到的进行更新 // 1找得到的进行更新
Long existsSkuId = existsSkuMap.remove(propertiesKey); Long existsSkuId = existsSkuMap.remove(propertiesKey);
if (existsSkuId != null) { if (existsSkuId != null) {
sku.setId(existsSkuId); sku.setId(existsSkuId);
// TODO 那spuId岂不是为null了
updateSkus.add(sku); updateSkus.add(sku);
return; return;
} }

View File

@ -12,6 +12,7 @@ import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert;
import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
import cn.iocoder.yudao.module.product.dal.mysql.spu.ProductSpuMapper; import cn.iocoder.yudao.module.product.dal.mysql.spu.ProductSpuMapper;
import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
import cn.iocoder.yudao.module.product.service.brand.ProductBrandService; import cn.iocoder.yudao.module.product.service.brand.ProductBrandService;
import cn.iocoder.yudao.module.product.service.category.ProductCategoryService; import cn.iocoder.yudao.module.product.service.category.ProductCategoryService;
import cn.iocoder.yudao.module.product.service.sku.ProductSkuService; import cn.iocoder.yudao.module.product.service.sku.ProductSkuService;
@ -21,10 +22,7 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.Collection; import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 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.*;
@ -54,20 +52,21 @@ public class ProductSpuServiceImpl implements ProductSpuService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public Long createSpu(ProductSpuCreateReqVO createReqVO) { public Long createSpu(ProductSpuCreateReqVO createReqVO) {
// 校验分类 // 校验分类 TODO 暂不清楚为什么只能选择第三层的结点
validateCategory(createReqVO.getCategoryId()); //validateCategory(createReqVO.getCategoryId());
// 校验品牌 // 校验品牌 TODO 暂不校验
brandService.validateProductBrand(createReqVO.getBrandId()); //brandService.validateProductBrand(createReqVO.getBrandId());
// 校验SKU
List<ProductSkuCreateOrUpdateReqVO> skuSaveReqList = createReqVO.getSkus();
productSkuService.validateSkuList(skuSaveReqList, createReqVO.getSpecType());
// 插入 SPU List<ProductSkuCreateOrUpdateReqVO> skuSaveReqList = createReqVO.getSkus();
// 校验SKU
productSkuService.validateSkuList(skuSaveReqList, createReqVO.getSpecType());
ProductSpuDO spu = ProductSpuConvert.INSTANCE.convert(createReqVO); ProductSpuDO spu = ProductSpuConvert.INSTANCE.convert(createReqVO);
// 初始化SPU中SKU相关属性
initSpuFromSkus(spu, skuSaveReqList); initSpuFromSkus(spu, skuSaveReqList);
// 插入 SPU
productSpuMapper.insert(spu); productSpuMapper.insert(spu);
// 插入 SKU // 插入 SKU
productSkuService.createSkuList(spu.getId(), spu.getName(), skuSaveReqList); productSkuService.createSkuList(spu.getId(), skuSaveReqList);
// 返回 // 返回
return spu.getId(); return spu.getId();
} }
@ -90,7 +89,7 @@ public class ProductSpuServiceImpl implements ProductSpuService {
initSpuFromSkus(updateObj, skuSaveReqList); initSpuFromSkus(updateObj, skuSaveReqList);
productSpuMapper.updateById(updateObj); productSpuMapper.updateById(updateObj);
// 批量更新 SKU // 批量更新 SKU
productSkuService.updateSkuList(updateObj.getId(), updateObj.getName(), updateReqVO.getSkus()); productSkuService.updateSkuList(updateObj.getId(), updateReqVO.getSkus());
} }
/** /**
@ -101,11 +100,25 @@ public class ProductSpuServiceImpl implements ProductSpuService {
* @param skus 商品 SKU 数组 * @param skus 商品 SKU 数组
*/ */
private void initSpuFromSkus(ProductSpuDO spu, List<ProductSkuCreateOrUpdateReqVO> skus) { private void initSpuFromSkus(ProductSpuDO spu, List<ProductSkuCreateOrUpdateReqVO> skus) {
spu.setMarketPrice(getMaxValue(skus, ProductSkuCreateOrUpdateReqVO::getMarketPrice)); // 断言避免告警
// TODO ProductSpuDO中已没有相关属性 assert skus.size() > 0;
//spu.setMaxPrice(getMaxValue(skus, ProductSkuCreateOrUpdateReqVO::getPrice)); // 获取sku单价最低的商品
//spu.setMinPrice(getMinValue(skus, ProductSkuCreateOrUpdateReqVO::getPrice)); ProductSkuCreateOrUpdateReqVO vo = skus.stream().min(Comparator.comparing(ProductSkuCreateOrUpdateReqVO::getPrice)).get();
//spu.setTotalStock(getSumValue(skus, ProductSkuCreateOrUpdateReqVO::getStock, Integer::sum)); // sku单价最低的商品的价格
spu.setPrice(vo.getPrice());
// sku单价最低的商品的市场价格
spu.setMarketPrice(vo.getMarketPrice());
// sku单价最低的商品的成本价格
spu.setCostPrice(vo.getCostPrice());
// sku单价最低的商品的条形码
spu.setBarCode(vo.getBarCode());
// 默认状态为上架
spu.setStatus(ProductSpuStatusEnum.ENABLE.getStatus());
// TODO 默认商品销量和浏览量为零
spu.setSalesCount(0);
spu.setBrowseCount(0);
// skus库存总数
spu.setStock(getSumValue(skus, ProductSkuCreateOrUpdateReqVO::getStock, Integer::sum));
} }
/** /**
@ -155,14 +168,14 @@ public class ProductSpuServiceImpl implements ProductSpuService {
@Override @Override
public PageResult<ProductSpuDO> getSpuPage(ProductSpuPageReqVO pageReqVO) { public PageResult<ProductSpuDO> getSpuPage(ProductSpuPageReqVO pageReqVO) {
// 库存告警的 SPU 编号的集合 // 库存告警的 SPU 编号的集合 TODO 一个接口一个接口来
Set<Long> alarmStockSpuIds = null; Set<Long> alarmStockSpuIds = null;
if (Boolean.TRUE.equals(pageReqVO.getAlarmStock())) { //if (Boolean.TRUE.equals(pageReqVO.getAlarmStock())) {
alarmStockSpuIds = CollectionUtils.convertSet(productSkuService.getSkuListByAlarmStock(), ProductSkuDO::getSpuId); // alarmStockSpuIds = CollectionUtils.convertSet(productSkuService.getSkuListByAlarmStock(), ProductSkuDO::getSpuId);
if (CollUtil.isEmpty(alarmStockSpuIds)) { // if (CollUtil.isEmpty(alarmStockSpuIds)) {
return PageResult.empty(); // return PageResult.empty();
} // }
} //}
// 分页查询 // 分页查询
return productSpuMapper.selectPage(pageReqVO, alarmStockSpuIds); return productSpuMapper.selectPage(pageReqVO, alarmStockSpuIds);
} }

View File

@ -75,7 +75,7 @@ public class ProductSkuServiceTest extends BaseDbUnitTest {
); );
// 调用 // 调用
productSkuService.updateSkuList(spuId, spuName, skus); productSkuService.updateSkuList(spuId, skus);
// 断言 // 断言
List<ProductSkuDO> dbSkus = productSkuMapper.selectListBySpuId(spuId); List<ProductSkuDO> dbSkus = productSkuMapper.selectListBySpuId(spuId);
assertEquals(dbSkus.size(), 2); assertEquals(dbSkus.size(), 2);

View File

@ -9,10 +9,7 @@ import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.collection.SetUtils; import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO; import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO;
import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuCreateReqVO; import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*;
import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuPageReqVO;
import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuRespVO;
import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuUpdateReqVO;
import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO; import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO;
import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert; import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert;
import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
@ -37,6 +34,7 @@ import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getSumValue;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
@ -83,21 +81,9 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest {
// 准备参数 // 准备参数
ProductSpuCreateReqVO createReqVO = randomPojo(ProductSpuCreateReqVO.class, o -> { ProductSpuCreateReqVO createReqVO = randomPojo(ProductSpuCreateReqVO.class, o -> {
o.setSpecType(true); o.setSpecType(true);
o.setStatus(ProductSpuStatusEnum.ENABLE.getStatus());
}); });
// 校验SKU
List<ProductSkuCreateOrUpdateReqVO> skuCreateReqList = createReqVO.getSkus();
Long spu = productSpuService.createSpu(createReqVO); Long spu = productSpuService.createSpu(createReqVO);
ProductSpuDO productSpuDO = productSpuMapper.selectById(spu); ProductSpuDO productSpuDO = productSpuMapper.selectById(spu);
createReqVO.setMarketPrice(CollectionUtils.getMaxValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getMarketPrice));
// TODO ProductSpuCreateReqVO中已没有相关属性
// createReqVO.setMaxPrice(CollectionUtils.getMaxValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getPrice));
// createReqVO.setMinPrice(CollectionUtils.getMinValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getPrice));
// createReqVO.setTotalStock(CollectionUtils.getSumValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getStock, Integer::sum));
assertPojoEquals(createReqVO, productSpuDO); assertPojoEquals(createReqVO, productSpuDO);
} }
@ -111,18 +97,9 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest {
ProductSpuUpdateReqVO reqVO = randomPojo(ProductSpuUpdateReqVO.class, o -> { ProductSpuUpdateReqVO reqVO = randomPojo(ProductSpuUpdateReqVO.class, o -> {
o.setId(createReqVO.getId()); // 设置更新的 ID o.setId(createReqVO.getId()); // 设置更新的 ID
o.setSpecType(true); o.setSpecType(true);
o.setStatus(ProductSpuStatusEnum.DISABLE.getStatus());
}); });
// 调用 // 调用
productSpuService.updateSpu(reqVO); productSpuService.updateSpu(reqVO);
List<ProductSkuCreateOrUpdateReqVO> skuCreateReqList = reqVO.getSkus();
reqVO.setMarketPrice(CollectionUtils.getMaxValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getMarketPrice));
// TODO ProductSpuUpdateReqVO中已没有相关属性
// reqVO.setMaxPrice(CollectionUtils.getMaxValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getPrice));
// reqVO.setMinPrice(CollectionUtils.getMinValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getPrice));
// reqVO.setTotalStock(CollectionUtils.getSumValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getStock, Integer::sum));
// 校验是否更新正确 // 校验是否更新正确
ProductSpuDO spu = productSpuMapper.selectById(reqVO.getId()); // 获取最新的 ProductSpuDO spu = productSpuMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, spu); assertPojoEquals(reqVO, spu);
@ -132,7 +109,6 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest {
public void testValidateSpuExists_exception() { public void testValidateSpuExists_exception() {
ProductSpuUpdateReqVO reqVO = randomPojo(ProductSpuUpdateReqVO.class, o -> { ProductSpuUpdateReqVO reqVO = randomPojo(ProductSpuUpdateReqVO.class, o -> {
o.setSpecType(true); o.setSpecType(true);
o.setStatus(ProductSpuStatusEnum.DISABLE.getStatus());
}); });
// 调用 // 调用
Assertions.assertThrows(ServiceException.class, () -> productSpuService.updateSpu(reqVO)); Assertions.assertThrows(ServiceException.class, () -> productSpuService.updateSpu(reqVO));
@ -175,7 +151,7 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest {
void getSpuPage_alarmStock_empty() { void getSpuPage_alarmStock_empty() {
// 调用 // 调用
ProductSpuPageReqVO productSpuPageReqVO = new ProductSpuPageReqVO(); ProductSpuPageReqVO productSpuPageReqVO = new ProductSpuPageReqVO();
productSpuPageReqVO.setAlarmStock(true); //productSpuPageReqVO.setAlarmStock(true);
PageResult<ProductSpuDO> spuPage = productSpuService.getSpuPage(productSpuPageReqVO); PageResult<ProductSpuDO> spuPage = productSpuService.getSpuPage(productSpuPageReqVO);
@ -225,10 +201,10 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest {
// 调用 // 调用
ProductSpuPageReqVO productSpuPageReqVO = new ProductSpuPageReqVO(); ProductSpuPageReqVO productSpuPageReqVO = new ProductSpuPageReqVO();
productSpuPageReqVO.setAlarmStock(true); //productSpuPageReqVO.setAlarmStock(true);
PageResult<ProductSpuDO> spuPage = productSpuService.getSpuPage(productSpuPageReqVO); PageResult<ProductSpuDO> spuPage = productSpuService.getSpuPage(productSpuPageReqVO);
PageResult<ProductSpuRespVO> result = ProductSpuConvert.INSTANCE.convertPage(productSpuMapper.selectPage(productSpuPageReqVO, alarmStockSpuIds)); PageResult<ProductSpuPageRespVO> result = ProductSpuConvert.INSTANCE.convertPage(productSpuMapper.selectPage(productSpuPageReqVO, alarmStockSpuIds));
Assertions.assertIterableEquals(result.getList(), spuPage.getList()); Assertions.assertIterableEquals(result.getList(), spuPage.getList());
assertEquals(spuPage.getTotal(), result.getTotal()); assertEquals(spuPage.getTotal(), result.getTotal());
} }
@ -273,14 +249,14 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest {
// 调用 // 调用
ProductSpuPageReqVO productSpuPageReqVO = new ProductSpuPageReqVO(); ProductSpuPageReqVO productSpuPageReqVO = new ProductSpuPageReqVO();
productSpuPageReqVO.setAlarmStock(false); //productSpuPageReqVO.setAlarmStock(false);
productSpuPageReqVO.setBrandId(brandId); //productSpuPageReqVO.setBrandId(brandId);
productSpuPageReqVO.setStatus(ProductSpuStatusEnum.ENABLE.getStatus()); //productSpuPageReqVO.setStatus(ProductSpuStatusEnum.ENABLE.getStatus());
productSpuPageReqVO.setCategoryId(categoryId); productSpuPageReqVO.setCategoryId(categoryId);
PageResult<ProductSpuDO> spuPage = productSpuService.getSpuPage(productSpuPageReqVO); PageResult<ProductSpuDO> spuPage = productSpuService.getSpuPage(productSpuPageReqVO);
PageResult<ProductSpuRespVO> result = ProductSpuConvert.INSTANCE.convertPage(productSpuMapper.selectPage(productSpuPageReqVO, (Set<Long>) null)); PageResult<ProductSpuPageRespVO> result = ProductSpuConvert.INSTANCE.convertPage(productSpuMapper.selectPage(productSpuPageReqVO, (Set<Long>) null));
assertEquals(result, spuPage); assertEquals(result, spuPage);
} }