Commit 0bc2ef1d39ad12b1f3ba8b0adf9d185b9f57619d

Authored by luowenfeng
1 parent c2fd24f5

feature(uniapp商品): 商品业务代码调整

yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java
@@ -173,6 +173,23 @@ public class CollectionUtils { @@ -173,6 +173,23 @@ public class CollectionUtils {
173 return valueFunc.apply(t); 173 return valueFunc.apply(t);
174 } 174 }
175 175
  176 + public static <T, V extends Comparable<? super V>> V getMinValue(List<T> from, Function<T, V> valueFunc) {
  177 + if (CollUtil.isEmpty(from)) {
  178 + return null;
  179 + }
  180 + assert from.size() > 0; // 断言,避免告警
  181 + T t = from.stream().min(Comparator.comparing(valueFunc)).get();
  182 + return valueFunc.apply(t);
  183 + }
  184 +
  185 + public static <T, V extends Comparable<? super V>> V getSumValue(List<T> from, Function<T, V> valueFunc, BinaryOperator<V> accumulator) {
  186 + if (CollUtil.isEmpty(from)) {
  187 + return null;
  188 + }
  189 + assert from.size() > 0; // 断言,避免告警
  190 + return from.stream().map(valueFunc).reduce(accumulator).get();
  191 + }
  192 +
176 public static <T> void addIfNotNull(Collection<T> coll, T item) { 193 public static <T> void addIfNotNull(Collection<T> coll, T item) {
177 if (item == null) { 194 if (item == null) {
178 return; 195 return;
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/ErrorCodeConstants.java
@@ -15,6 +15,7 @@ public interface ErrorCodeConstants { @@ -15,6 +15,7 @@ public interface ErrorCodeConstants {
15 ErrorCode PRODUCT_CATEGORY_PARENT_NOT_FIRST_LEVEL = new ErrorCode(1008001002, "父分类不能是二级分类"); 15 ErrorCode PRODUCT_CATEGORY_PARENT_NOT_FIRST_LEVEL = new ErrorCode(1008001002, "父分类不能是二级分类");
16 ErrorCode PRODUCT_CATEGORY_EXISTS_CHILDREN = new ErrorCode(1008001003, "存在子分类,无法删除"); 16 ErrorCode PRODUCT_CATEGORY_EXISTS_CHILDREN = new ErrorCode(1008001003, "存在子分类,无法删除");
17 ErrorCode PRODUCT_CATEGORY_DISABLED = new ErrorCode(1008001004, "商品分类({})已禁用,无法使用"); 17 ErrorCode PRODUCT_CATEGORY_DISABLED = new ErrorCode(1008001004, "商品分类({})已禁用,无法使用");
  18 + ErrorCode PRODUCT_CATEGORY_LEVEL = new ErrorCode(1008001005, "商品需挂在三级分类下");
18 19
19 // ========== 品牌相关编号 1008002000 ========== 20 // ========== 品牌相关编号 1008002000 ==========
20 ErrorCode PRODUCT_BRAND_NOT_EXISTS = new ErrorCode(1008002000, "品牌不存在"); 21 ErrorCode PRODUCT_BRAND_NOT_EXISTS = new ErrorCode(1008002000, "品牌不存在");
@@ -31,4 +32,8 @@ public interface ErrorCodeConstants { @@ -31,4 +32,8 @@ public interface ErrorCodeConstants {
31 // ========== 商品sku 1008006000 ========== 32 // ========== 商品sku 1008006000 ==========
32 ErrorCode SKU_NOT_EXISTS = new ErrorCode(1008006000, "商品sku不存在"); 33 ErrorCode SKU_NOT_EXISTS = new ErrorCode(1008006000, "商品sku不存在");
33 ErrorCode SKU_PROPERTIES_DUPLICATED = new ErrorCode(1008006001, "商品sku的属性组合存在重复"); 34 ErrorCode SKU_PROPERTIES_DUPLICATED = new ErrorCode(1008006001, "商品sku的属性组合存在重复");
  35 +
  36 + ErrorCode PRODUCT_SPU_ATTR_NUMBERS_MUST_BE_EQUALS = new ErrorCode(1008006002, "一个 Spu 下的每个 SKU ,其规格数必须一致");
  37 +
  38 + ErrorCode PRODUCT_SPU_SKU_NOT_DUPLICATE = new ErrorCode(1008006003, "一个 SPU 下的每个 SKU ,必须不重复");
34 } 39 }
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/category/ProductCategoryServiceImpl.java
@@ -93,6 +93,14 @@ public class ProductCategoryServiceImpl implements ProductCategoryService { @@ -93,6 +93,14 @@ public class ProductCategoryServiceImpl implements ProductCategoryService {
93 93
94 @Override 94 @Override
95 public void validateProductCategory(Long id) { 95 public void validateProductCategory(Long id) {
  96 + Integer level = categoryLevel(id, 1);
  97 + if(level < 3){
  98 + throw exception(PRODUCT_CATEGORY_LEVEL);
  99 + }
  100 + }
  101 +
  102 + // 校验分类级别
  103 + private Integer categoryLevel(Long id, int level){
96 ProductCategoryDO category = productCategoryMapper.selectById(id); 104 ProductCategoryDO category = productCategoryMapper.selectById(id);
97 if (category == null) { 105 if (category == null) {
98 throw exception(PRODUCT_CATEGORY_NOT_EXISTS); 106 throw exception(PRODUCT_CATEGORY_NOT_EXISTS);
@@ -100,6 +108,10 @@ public class ProductCategoryServiceImpl implements ProductCategoryService { @@ -100,6 +108,10 @@ public class ProductCategoryServiceImpl implements ProductCategoryService {
100 if (ObjectUtil.notEqual(category.getStatus(), CommonStatusEnum.ENABLE.getStatus())) { 108 if (ObjectUtil.notEqual(category.getStatus(), CommonStatusEnum.ENABLE.getStatus())) {
101 throw exception(PRODUCT_CATEGORY_DISABLED); 109 throw exception(PRODUCT_CATEGORY_DISABLED);
102 } 110 }
  111 + if(category.getParentId() == 0) {
  112 + return level;
  113 + }
  114 + return categoryLevel(category.getParentId(), ++level);
103 } 115 }
104 116
105 @Override 117 @Override
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuService.java
@@ -68,14 +68,14 @@ public interface ProductSkuService { @@ -68,14 +68,14 @@ public interface ProductSkuService {
68 * 68 *
69 * @param list sku组合的集合 69 * @param list sku组合的集合
70 */ 70 */
71 - void validateProductSkus(List<ProductSkuCreateOrUpdateReqVO> list); 71 + void validateProductSkus(List<ProductSkuCreateOrUpdateReqVO> list, Integer specType);
72 72
73 /** 73 /**
74 * 批量创建 SKU 74 * 批量创建 SKU
75 * 75 *
76 * @param list SKU 对象集合 76 * @param list SKU 对象集合
77 */ 77 */
78 - void createProductSkus(List<ProductSkuDO> list); 78 + void createProductSkus(List<ProductSkuCreateOrUpdateReqVO> list, Long spuId);
79 79
80 /** 80 /**
81 * 根据 SPU 编号,批量更新它的 SKU 信息 81 * 根据 SPU 编号,批量更新它的 SKU 信息
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceImpl.java
1 package cn.iocoder.yudao.module.product.service.sku; 1 package cn.iocoder.yudao.module.product.service.sku;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
4 import cn.iocoder.yudao.module.product.controller.admin.property.vo.ProductPropertyRespVO; 5 import cn.iocoder.yudao.module.product.controller.admin.property.vo.ProductPropertyRespVO;
5 import cn.iocoder.yudao.module.product.controller.admin.propertyvalue.vo.ProductPropertyValueRespVO; 6 import cn.iocoder.yudao.module.product.controller.admin.propertyvalue.vo.ProductPropertyValueRespVO;
6 import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuBaseVO; 7 import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuBaseVO;
@@ -11,6 +12,7 @@ import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert; @@ -11,6 +12,7 @@ import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert;
11 import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; 12 import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
12 import cn.iocoder.yudao.module.product.dal.mysql.sku.ProductSkuMapper; 13 import cn.iocoder.yudao.module.product.dal.mysql.sku.ProductSkuMapper;
13 import cn.iocoder.yudao.module.product.enums.ErrorCodeConstants; 14 import cn.iocoder.yudao.module.product.enums.ErrorCodeConstants;
  15 +import cn.iocoder.yudao.module.product.enums.spu.ProductSpuSpecTypeEnum;
14 import cn.iocoder.yudao.module.product.service.property.ProductPropertyService; 16 import cn.iocoder.yudao.module.product.service.property.ProductPropertyService;
15 import org.springframework.stereotype.Service; 17 import org.springframework.stereotype.Service;
16 import org.springframework.transaction.annotation.Transactional; 18 import org.springframework.transaction.annotation.Transactional;
@@ -86,45 +88,52 @@ public class ProductSkuServiceImpl implements ProductSkuService { @@ -86,45 +88,52 @@ public class ProductSkuServiceImpl implements ProductSkuService {
86 return productSkuMapper.selectPage(pageReqVO); 88 return productSkuMapper.selectPage(pageReqVO);
87 } 89 }
88 90
89 - // TODO luowenfeng:参考下 yudao-cloud 的 checkProductAttr 方法,重构下  
90 @Override 91 @Override
91 - public void validateProductSkus(List<ProductSkuCreateOrUpdateReqVO> list) {  
92 - List<ProductSkuBaseVO.Property> skuPropertyList = list.stream().flatMap(p -> Optional.of(p.getProperties()).orElse(new ArrayList<>()).stream()).collect(Collectors.toList());  
93 - // 校验规格属性存在  
94 - // TODO @luowenfeng:使用 CollectionUtils.convert  
95 - List<Long> propertyIds = skuPropertyList.stream().map(ProductSkuBaseVO.Property::getPropertyId).collect(Collectors.toList());  
96 - List<ProductPropertyRespVO> propertyAndValueList = productPropertyService.selectByIds(propertyIds);  
97 - // TODO @luowenfeng:校验数量一致;  
98 - if (propertyAndValueList.isEmpty()) {  
99 - throw exception(PROPERTY_NOT_EXISTS);  
100 - }  
101 - // 校验规格属性值存在  
102 - // TODO @luowenfeng:使用 CollectionUtils.convert  
103 - Map<Long, ProductPropertyRespVO> propertyMap = propertyAndValueList.stream().collect(Collectors.toMap(ProductPropertyRespVO::getId, p -> p));  
104 - skuPropertyList.forEach(p -> {  
105 - ProductPropertyRespVO productPropertyRespVO = propertyMap.get(p.getPropertyId());  
106 - // 如果对应的属性名不存在或属性名下的属性值集合为空,给出提示  
107 - if (null == productPropertyRespVO || productPropertyRespVO.getPropertyValueList().isEmpty()) { 92 + public void validateProductSkus(List<ProductSkuCreateOrUpdateReqVO> list, Integer specType) {
  93 + // 多规格才需校验
  94 + if(specType.equals(ProductSpuSpecTypeEnum.DISABLE.getType())){
  95 + List<ProductSkuBaseVO.Property> skuPropertyList = list.stream().flatMap(p -> Optional.of(p.getProperties()).orElse(new ArrayList<>()).stream()).collect(Collectors.toList());
  96 + // 1、校验规格属性存在
  97 + List<Long> propertyIds = CollectionUtils.convertList(skuPropertyList, ProductSkuBaseVO.Property::getPropertyId);
  98 + List<ProductPropertyRespVO> propertyAndValueList = productPropertyService.selectByIds(propertyIds);
  99 + if (propertyAndValueList.size() == propertyIds.size()){
108 throw exception(PROPERTY_NOT_EXISTS); 100 throw exception(PROPERTY_NOT_EXISTS);
109 } 101 }
110 - // 判断改属性名对应的属性值是否存在,不存在,给出提示  
111 - if (!productPropertyRespVO.getPropertyValueList().stream().map(ProductPropertyValueRespVO::getId).collect(Collectors.toSet()).contains(p.getValueId())) {  
112 - throw exception(ErrorCodeConstants.PROPERTY_VALUE_NOT_EXISTS); 102 + // 2. 校验,一个 Sku 下,没有重复的规格。校验方式是,遍历每个 Sku ,看看是否有重复的规格 attrId
  103 + List<ProductPropertyValueRespVO> collect = propertyAndValueList.stream()
  104 + .flatMap(v -> Optional.of(v.getPropertyValueList())
  105 + .orElse(new ArrayList<>()).stream()).collect(Collectors.toList());
  106 + Map<Long, ProductPropertyValueRespVO> propertyValueRespVOMap = CollectionUtils.convertMap(collect, ProductPropertyValueRespVO::getId);
  107 + list.forEach(v->{
  108 + Set<Long> keys = v.getProperties().stream().map(k -> propertyValueRespVOMap.get(k.getValueId()).getPropertyId()).collect(Collectors.toSet());
  109 + if(keys.size() != v.getProperties().size()){
  110 + throw exception(ErrorCodeConstants.SKU_PROPERTIES_DUPLICATED);
  111 + }
  112 + });
  113 +
  114 + // 3. 再校验,每个 Sku 的规格值的数量,是一致的。
  115 + int attrValueIdsSize = list.get(0).getProperties().size();
  116 + for (int i = 1; i < list.size(); i++) {
  117 + if (attrValueIdsSize != list.get(i).getProperties().size()) {
  118 + throw exception(ErrorCodeConstants.PRODUCT_SPU_ATTR_NUMBERS_MUST_BE_EQUALS);
  119 + }
113 } 120 }
114 - });  
115 - // 校验是否有重复的sku组合  
116 - List<List<ProductSkuBaseVO.Property>> skuProperties = list.stream().map(ProductSkuBaseVO::getProperties).collect(Collectors.toList());  
117 - Set<String> skuPropertiesConvertSet = new HashSet<>();  
118 - skuProperties.forEach(p -> {  
119 - // 组合属性值id为 1~2~3.... 形式的字符串,通过set的特性判断是否有重复的组合  
120 - if (!skuPropertiesConvertSet.add(p.stream().map(pr -> String.valueOf(pr.getValueId())).sorted().collect(Collectors.joining("~")))) {  
121 - throw exception(ErrorCodeConstants.SKU_PROPERTIES_DUPLICATED); 121 +
  122 + // 4. 最后校验,每个 Sku 之间不是重复的
  123 + Set<Set<Long>> skuAttrValues = new HashSet<>(); // 每个元素,都是一个 Sku 的 attrValueId 集合。这样,通过最外层的 Set ,判断是否有重复的.
  124 + for (ProductSkuCreateOrUpdateReqVO sku : list) {
  125 + if (!skuAttrValues.add(sku.getProperties().stream().map(ProductSkuBaseVO.Property::getValueId).collect(Collectors.toSet()))) { // 添加失败,说明重复
  126 + throw exception(ErrorCodeConstants.PRODUCT_SPU_SKU_NOT_DUPLICATE);
  127 + }
122 } 128 }
123 - }); 129 + }
124 } 130 }
125 131
126 @Override 132 @Override
127 - public void createProductSkus(List<ProductSkuDO> skuDOList) { 133 + public void createProductSkus(List<ProductSkuCreateOrUpdateReqVO> skuCreateReqList, Long spuId) {
  134 + // 批量插入 SKU
  135 + List<ProductSkuDO> skuDOList = ProductSkuConvert.INSTANCE.convertSkuDOList(skuCreateReqList);
  136 + skuDOList.forEach(v->v.setSpuId(spuId));
128 productSkuMapper.insertBatch(skuDOList); 137 productSkuMapper.insertBatch(skuDOList);
129 } 138 }
130 139
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java
1 package cn.iocoder.yudao.module.product.service.spu; 1 package cn.iocoder.yudao.module.product.service.spu;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
4 import cn.iocoder.yudao.module.product.controller.admin.property.vo.ProductPropertyRespVO; 5 import cn.iocoder.yudao.module.product.controller.admin.property.vo.ProductPropertyRespVO;
5 import cn.iocoder.yudao.module.product.controller.admin.property.vo.ProductPropertyViewRespVO; 6 import cn.iocoder.yudao.module.product.controller.admin.property.vo.ProductPropertyViewRespVO;
6 import cn.iocoder.yudao.module.product.controller.admin.propertyvalue.vo.ProductPropertyValueRespVO; 7 import cn.iocoder.yudao.module.product.controller.admin.propertyvalue.vo.ProductPropertyValueRespVO;
@@ -58,31 +59,23 @@ public class ProductSpuServiceImpl implements ProductSpuService { @@ -58,31 +59,23 @@ public class ProductSpuServiceImpl implements ProductSpuService {
58 @Transactional 59 @Transactional
59 public Long createProductSpu(ProductSpuCreateReqVO createReqVO) { 60 public Long createProductSpu(ProductSpuCreateReqVO createReqVO) {
60 // 校验分类 61 // 校验分类
61 - // TODO @luowenfeng:可以在这个类里加个方法,校验分类;商品必须挂在三级分类下;  
62 categoryService.validateProductCategory(createReqVO.getCategoryId()); 62 categoryService.validateProductCategory(createReqVO.getCategoryId());
63 // TODO @luowenfeng:校验品牌 63 // TODO @luowenfeng:校验品牌
  64 +
64 // 校验SKU 65 // 校验SKU
65 List<ProductSkuCreateOrUpdateReqVO> skuCreateReqList = createReqVO.getSkus(); 66 List<ProductSkuCreateOrUpdateReqVO> skuCreateReqList = createReqVO.getSkus();
66 - // 多规格才需校验  
67 - // TODO @luowenfeng:可以把 type 传递到 productSkuService 里,通过它统一判断处理  
68 - if(Objects.equals(createReqVO.getSpecType(), ProductSpuSpecTypeEnum.DISABLE.getType())) {  
69 - productSkuService.validateProductSkus(skuCreateReqList);  
70 - } 67 + productSkuService.validateProductSkus(skuCreateReqList, createReqVO.getSpecType());
71 68
72 // 插入 SPU 69 // 插入 SPU
73 ProductSpuDO spu = ProductSpuConvert.INSTANCE.convert(createReqVO); 70 ProductSpuDO spu = ProductSpuConvert.INSTANCE.convert(createReqVO);
74 - // TODO @luowenfeng:可以在 CollectionUtils 增加 getMaxValue 方法,增加一个 defaultValue 方法,如果为空,则返回 defaultValue  
75 - spu.setMarketPrice(skuCreateReqList.stream().map(ProductSkuCreateOrUpdateReqVO::getMarketPrice).max(Integer::compare).orElse(0));  
76 - spu.setMaxPrice(skuCreateReqList.stream().map(ProductSkuCreateOrUpdateReqVO::getPrice).max(Integer::compare).orElse(0));  
77 - spu.setMinPrice(skuCreateReqList.stream().map(ProductSkuCreateOrUpdateReqVO::getPrice).min(Integer::compare).orElse(0));  
78 - // TODO @luowenfeng:库存求和 71 + spu.setMarketPrice(CollectionUtils.getMaxValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getMarketPrice));
  72 + spu.setMaxPrice(CollectionUtils.getMaxValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getPrice));
  73 + spu.setMinPrice(CollectionUtils.getMinValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getPrice));
  74 + spu.setTotalStock(CollectionUtils.getSumValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getStock, Integer::sum));
79 ProductSpuMapper.insert(spu); 75 ProductSpuMapper.insert(spu);
80 76
81 - // 批量插入 SKU  
82 - // TODO @luowenfeng:convert 逻辑,交给 createProductSkus 一起处理  
83 - List<ProductSkuDO> skuDOList = ProductSkuConvert.INSTANCE.convertSkuDOList(skuCreateReqList);  
84 - skuDOList.forEach(v->v.setSpuId(spu.getId()));  
85 - productSkuService.createProductSkus(skuDOList); 77 + // 插入 SKU
  78 + productSkuService.createProductSkus(skuCreateReqList, spu.getId());
86 // 返回 79 // 返回
87 return spu.getId(); 80 return spu.getId();
88 } 81 }
@@ -98,10 +91,7 @@ public class ProductSpuServiceImpl implements ProductSpuService { @@ -98,10 +91,7 @@ public class ProductSpuServiceImpl implements ProductSpuService {
98 // 校验SKU 91 // 校验SKU
99 List<ProductSkuCreateOrUpdateReqVO> skuCreateReqList = updateReqVO.getSkus(); 92 List<ProductSkuCreateOrUpdateReqVO> skuCreateReqList = updateReqVO.getSkus();
100 // 多规格才需校验 93 // 多规格才需校验
101 - // TODO @luowenfeng:可以把 type 传递到 productSkuService 里,通过它统一判断处理  
102 - if(updateReqVO.getSpecType().equals(ProductSpuSpecTypeEnum.DISABLE.getType())) {  
103 - productSkuService.validateProductSkus(skuCreateReqList);  
104 - } 94 + productSkuService.validateProductSkus(skuCreateReqList, updateReqVO.getSpecType());
105 95
106 // 更新 SPU 96 // 更新 SPU
107 ProductSpuDO updateObj = ProductSpuConvert.INSTANCE.convert(updateReqVO); 97 ProductSpuDO updateObj = ProductSpuConvert.INSTANCE.convert(updateReqVO);