Commit c2fd24f597003ea04ff8e726c85c169735f8e0c3
1 parent
edfe3798
mall:code review 商品 spu 的后端代码
Showing
8 changed files
with
74 additions
and
49 deletions
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuBaseVO.java
| ... | ... | @@ -21,7 +21,9 @@ public class ProductSkuBaseVO { |
| 21 | 21 | @NotEmpty(message = "商品 SKU 名字不能为空") |
| 22 | 22 | private String name; |
| 23 | 23 | |
| 24 | - @ApiModelProperty(value = "规格值数组-json格式 单规格中无此列表, [{propertyId: , valueId: }, {propertyId: , valueId: }]") | |
| 24 | + /** | |
| 25 | + * 规格值数组 | |
| 26 | + */ | |
| 25 | 27 | private List<Property> properties; |
| 26 | 28 | |
| 27 | 29 | @ApiModelProperty(value = "销售价格,单位:分", required = true, example = "1024", notes = "单位:分") | ... | ... |
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.java
| ... | ... | @@ -35,15 +35,15 @@ public class ProductSpuController { |
| 35 | 35 | @PostMapping("/create") |
| 36 | 36 | @ApiOperation("创建商品 SPU") |
| 37 | 37 | @PreAuthorize("@ss.hasPermission('product:spu:create')") |
| 38 | - public CommonResult<Long> createSpu(@Valid @RequestBody ProductSpuCreateReqVO createReqVO) { | |
| 39 | - return success(spuService.createSpu(createReqVO)); | |
| 38 | + public CommonResult<Long> createProductSpu(@Valid @RequestBody ProductSpuCreateReqVO createReqVO) { | |
| 39 | + return success(spuService.createProductSpu(createReqVO)); | |
| 40 | 40 | } |
| 41 | 41 | |
| 42 | 42 | @PutMapping("/update") |
| 43 | 43 | @ApiOperation("更新商品 SPU") |
| 44 | 44 | @PreAuthorize("@ss.hasPermission('product:spu:update')") |
| 45 | 45 | public CommonResult<Boolean> updateSpu(@Valid @RequestBody ProductSpuUpdateReqVO updateReqVO) { |
| 46 | - spuService.updateSpu(updateReqVO); | |
| 46 | + spuService.updateProductSpu(updateReqVO); | |
| 47 | 47 | return success(true); |
| 48 | 48 | } |
| 49 | 49 | ... | ... |
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/sku/ProductSkuMapper.java
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuService.java
| ... | ... | @@ -68,14 +68,22 @@ public interface ProductSkuService { |
| 68 | 68 | * |
| 69 | 69 | * @param list sku组合的集合 |
| 70 | 70 | */ |
| 71 | - void validateSkus(List<ProductSkuCreateOrUpdateReqVO> list); | |
| 71 | + void validateProductSkus(List<ProductSkuCreateOrUpdateReqVO> list); | |
| 72 | 72 | |
| 73 | 73 | /** |
| 74 | - * 批量保存 sku | |
| 74 | + * 批量创建 SKU | |
| 75 | 75 | * |
| 76 | - * @param list sku对象集合 | |
| 76 | + * @param list SKU 对象集合 | |
| 77 | 77 | */ |
| 78 | - void createSkus(List<ProductSkuDO> list); | |
| 78 | + void createProductSkus(List<ProductSkuDO> list); | |
| 79 | + | |
| 80 | + /** | |
| 81 | + * 根据 SPU 编号,批量更新它的 SKU 信息 | |
| 82 | + * | |
| 83 | + * @param spuId SPU 编码 | |
| 84 | + * @param skus SKU 的集合 | |
| 85 | + */ | |
| 86 | + void updateProductSkus(Long spuId, List<ProductSkuCreateOrUpdateReqVO> skus); | |
| 79 | 87 | |
| 80 | 88 | /** |
| 81 | 89 | * 获得商品 sku 集合 |
| ... | ... | @@ -100,11 +108,4 @@ public interface ProductSkuService { |
| 100 | 108 | */ |
| 101 | 109 | void deleteSkuBySpuId(Long spuId); |
| 102 | 110 | |
| 103 | - /** | |
| 104 | - * 根据 spuId 更新 spu 下的 sku 信息 | |
| 105 | - * | |
| 106 | - * @param spuId spu 编码 | |
| 107 | - * @param skus sku 的集合 | |
| 108 | - */ | |
| 109 | - void updateSkus(Long spuId, List<ProductSkuCreateOrUpdateReqVO> skus); | |
| 110 | 111 | } | ... | ... |
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceImpl.java
| 1 | 1 | package cn.iocoder.yudao.module.product.service.sku; |
| 2 | 2 | |
| 3 | -import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; | |
| 4 | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | 4 | import cn.iocoder.yudao.module.product.controller.admin.property.vo.ProductPropertyRespVO; |
| 6 | 5 | import cn.iocoder.yudao.module.product.controller.admin.propertyvalue.vo.ProductPropertyValueRespVO; |
| ... | ... | @@ -87,25 +86,30 @@ public class ProductSkuServiceImpl implements ProductSkuService { |
| 87 | 86 | return productSkuMapper.selectPage(pageReqVO); |
| 88 | 87 | } |
| 89 | 88 | |
| 89 | + // TODO luowenfeng:参考下 yudao-cloud 的 checkProductAttr 方法,重构下 | |
| 90 | 90 | @Override |
| 91 | - public void validateSkus(List<ProductSkuCreateOrUpdateReqVO> list) { | |
| 91 | + public void validateProductSkus(List<ProductSkuCreateOrUpdateReqVO> list) { | |
| 92 | 92 | List<ProductSkuBaseVO.Property> skuPropertyList = list.stream().flatMap(p -> Optional.of(p.getProperties()).orElse(new ArrayList<>()).stream()).collect(Collectors.toList()); |
| 93 | - // 校验规格属性以及规格值是否存在 | |
| 93 | + // 校验规格属性存在 | |
| 94 | + // TODO @luowenfeng:使用 CollectionUtils.convert | |
| 94 | 95 | List<Long> propertyIds = skuPropertyList.stream().map(ProductSkuBaseVO.Property::getPropertyId).collect(Collectors.toList()); |
| 95 | 96 | List<ProductPropertyRespVO> propertyAndValueList = productPropertyService.selectByIds(propertyIds); |
| 97 | + // TODO @luowenfeng:校验数量一致; | |
| 96 | 98 | if (propertyAndValueList.isEmpty()) { |
| 97 | - throw ServiceExceptionUtil.exception(PROPERTY_NOT_EXISTS); | |
| 99 | + throw exception(PROPERTY_NOT_EXISTS); | |
| 98 | 100 | } |
| 101 | + // 校验规格属性值存在 | |
| 102 | + // TODO @luowenfeng:使用 CollectionUtils.convert | |
| 99 | 103 | Map<Long, ProductPropertyRespVO> propertyMap = propertyAndValueList.stream().collect(Collectors.toMap(ProductPropertyRespVO::getId, p -> p)); |
| 100 | 104 | skuPropertyList.forEach(p -> { |
| 101 | 105 | ProductPropertyRespVO productPropertyRespVO = propertyMap.get(p.getPropertyId()); |
| 102 | 106 | // 如果对应的属性名不存在或属性名下的属性值集合为空,给出提示 |
| 103 | 107 | if (null == productPropertyRespVO || productPropertyRespVO.getPropertyValueList().isEmpty()) { |
| 104 | - throw ServiceExceptionUtil.exception(PROPERTY_NOT_EXISTS); | |
| 108 | + throw exception(PROPERTY_NOT_EXISTS); | |
| 105 | 109 | } |
| 106 | 110 | // 判断改属性名对应的属性值是否存在,不存在,给出提示 |
| 107 | 111 | if (!productPropertyRespVO.getPropertyValueList().stream().map(ProductPropertyValueRespVO::getId).collect(Collectors.toSet()).contains(p.getValueId())) { |
| 108 | - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PROPERTY_VALUE_NOT_EXISTS); | |
| 112 | + throw exception(ErrorCodeConstants.PROPERTY_VALUE_NOT_EXISTS); | |
| 109 | 113 | } |
| 110 | 114 | }); |
| 111 | 115 | // 校验是否有重复的sku组合 |
| ... | ... | @@ -114,13 +118,13 @@ public class ProductSkuServiceImpl implements ProductSkuService { |
| 114 | 118 | skuProperties.forEach(p -> { |
| 115 | 119 | // 组合属性值id为 1~2~3.... 形式的字符串,通过set的特性判断是否有重复的组合 |
| 116 | 120 | if (!skuPropertiesConvertSet.add(p.stream().map(pr -> String.valueOf(pr.getValueId())).sorted().collect(Collectors.joining("~")))) { |
| 117 | - throw ServiceExceptionUtil.exception(ErrorCodeConstants.SKU_PROPERTIES_DUPLICATED); | |
| 121 | + throw exception(ErrorCodeConstants.SKU_PROPERTIES_DUPLICATED); | |
| 118 | 122 | } |
| 119 | 123 | }); |
| 120 | 124 | } |
| 121 | 125 | |
| 122 | 126 | @Override |
| 123 | - public void createSkus(List<ProductSkuDO> skuDOList) { | |
| 127 | + public void createProductSkus(List<ProductSkuDO> skuDOList) { | |
| 124 | 128 | productSkuMapper.insertBatch(skuDOList); |
| 125 | 129 | } |
| 126 | 130 | |
| ... | ... | @@ -141,21 +145,23 @@ public class ProductSkuServiceImpl implements ProductSkuService { |
| 141 | 145 | |
| 142 | 146 | @Override |
| 143 | 147 | @Transactional |
| 144 | - public void updateSkus(Long spuId, List<ProductSkuCreateOrUpdateReqVO> skus) { | |
| 145 | - List<ProductSkuDO> allUpdateSkus = ProductSkuConvert.INSTANCE.convertSkuDOList(skus); | |
| 148 | + public void updateProductSkus(Long spuId, List<ProductSkuCreateOrUpdateReqVO> skus) { | |
| 146 | 149 | // 查询 spu 下已经存在的 sku 的集合 |
| 150 | + // TODO @luowenfeng:selectListBySpuId 搞个 | |
| 147 | 151 | List<ProductSkuDO> existsSkus = productSkuMapper.selectBySpuIds(Collections.singletonList(spuId)); |
| 148 | 152 | // TODO @franky:使用 CollUtils 即可 |
| 149 | 153 | Map<Long, ProductSkuDO> existsSkuMap = existsSkus.stream().collect(Collectors.toMap(ProductSkuDO::getId, p -> p)); |
| 150 | 154 | |
| 151 | - // 拆分三个集合, 新插入的, 需要更新的,需要删除的 | |
| 155 | + // 拆分三个集合,新插入的、需要更新的、需要删除的 | |
| 152 | 156 | List<ProductSkuDO> insertSkus = new ArrayList<>(); |
| 153 | 157 | List<ProductSkuDO> updateSkus = new ArrayList<>(); |
| 154 | 158 | List<ProductSkuDO> deleteSkus = new ArrayList<>(); |
| 155 | 159 | |
| 156 | 160 | // TODO @芋艿:是不是基于规格匹配会比较好。 |
| 161 | + List<ProductSkuDO> allUpdateSkus = ProductSkuConvert.INSTANCE.convertSkuDOList(skus); | |
| 157 | 162 | allUpdateSkus.forEach(p -> { |
| 158 | - if (null != p.getId()) { | |
| 163 | + if (p.getId() != null) { | |
| 164 | + // TODO @luowenfeng:contains | |
| 159 | 165 | if (existsSkuMap.get(p.getId()) != null) { |
| 160 | 166 | updateSkus.add(p); |
| 161 | 167 | return; |
| ... | ... | @@ -167,14 +173,13 @@ public class ProductSkuServiceImpl implements ProductSkuService { |
| 167 | 173 | insertSkus.add(p); |
| 168 | 174 | }); |
| 169 | 175 | |
| 176 | + // TODO @luowenfeng:使用 CollUtil.isNotEmpty 判断 | |
| 170 | 177 | if (insertSkus.size() > 0) { |
| 171 | 178 | productSkuMapper.insertBatch(insertSkus); |
| 172 | 179 | } |
| 173 | - | |
| 174 | 180 | if (updateSkus.size() > 0) { |
| 175 | 181 | updateSkus.forEach(p -> productSkuMapper.updateById(p)); |
| 176 | 182 | } |
| 177 | - | |
| 178 | 183 | if (deleteSkus.size() > 0) { |
| 179 | 184 | productSkuMapper.deleteBatchIds(deleteSkus.stream().map(ProductSkuDO::getId).collect(Collectors.toList())); |
| 180 | 185 | } | ... | ... |
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuService.java
| ... | ... | @@ -21,19 +21,19 @@ import java.util.List; |
| 21 | 21 | public interface ProductSpuService { |
| 22 | 22 | |
| 23 | 23 | /** |
| 24 | - * 创建商品spu | |
| 24 | + * 创建商品 SPU | |
| 25 | 25 | * |
| 26 | 26 | * @param createReqVO 创建信息 |
| 27 | 27 | * @return 编号 |
| 28 | 28 | */ |
| 29 | - Long createSpu(@Valid ProductSpuCreateReqVO createReqVO); | |
| 29 | + Long createProductSpu(@Valid ProductSpuCreateReqVO createReqVO); | |
| 30 | 30 | |
| 31 | 31 | /** |
| 32 | - * 更新商品spu | |
| 32 | + * 更新商品 SPU | |
| 33 | 33 | * |
| 34 | 34 | * @param updateReqVO 更新信息 |
| 35 | 35 | */ |
| 36 | - void updateSpu(@Valid ProductSpuUpdateReqVO updateReqVO); | |
| 36 | + void updateProductSpu(@Valid ProductSpuUpdateReqVO updateReqVO); | |
| 37 | 37 | |
| 38 | 38 | /** |
| 39 | 39 | * 删除商品spu | ... | ... |
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java
| ... | ... | @@ -7,7 +7,10 @@ import cn.iocoder.yudao.module.product.controller.admin.propertyvalue.vo.Product |
| 7 | 7 | import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuBaseVO; |
| 8 | 8 | import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO; |
| 9 | 9 | import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuRespVO; |
| 10 | -import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*; | |
| 10 | +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuCreateReqVO; | |
| 11 | +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuUpdateReqVO; | |
| 12 | +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.SpuPageReqVO; | |
| 13 | +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.SpuRespVO; | |
| 11 | 14 | import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageReqVO; |
| 12 | 15 | import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageRespVO; |
| 13 | 16 | import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert; |
| ... | ... | @@ -53,47 +56,60 @@ public class ProductSpuServiceImpl implements ProductSpuService { |
| 53 | 56 | |
| 54 | 57 | @Override |
| 55 | 58 | @Transactional |
| 56 | - public Long createSpu(ProductSpuCreateReqVO createReqVO) { | |
| 59 | + public Long createProductSpu(ProductSpuCreateReqVO createReqVO) { | |
| 57 | 60 | // 校验分类 |
| 61 | + // TODO @luowenfeng:可以在这个类里加个方法,校验分类;商品必须挂在三级分类下; | |
| 58 | 62 | categoryService.validateProductCategory(createReqVO.getCategoryId()); |
| 63 | + // TODO @luowenfeng:校验品牌 | |
| 59 | 64 | // 校验SKU |
| 60 | 65 | List<ProductSkuCreateOrUpdateReqVO> skuCreateReqList = createReqVO.getSkus(); |
| 61 | 66 | // 多规格才需校验 |
| 67 | + // TODO @luowenfeng:可以把 type 传递到 productSkuService 里,通过它统一判断处理 | |
| 62 | 68 | if(Objects.equals(createReqVO.getSpecType(), ProductSpuSpecTypeEnum.DISABLE.getType())) { |
| 63 | - productSkuService.validateSkus(skuCreateReqList); | |
| 69 | + productSkuService.validateProductSkus(skuCreateReqList); | |
| 64 | 70 | } |
| 65 | - // 插入SPU | |
| 71 | + | |
| 72 | + // 插入 SPU | |
| 66 | 73 | ProductSpuDO spu = ProductSpuConvert.INSTANCE.convert(createReqVO); |
| 74 | + // TODO @luowenfeng:可以在 CollectionUtils 增加 getMaxValue 方法,增加一个 defaultValue 方法,如果为空,则返回 defaultValue | |
| 67 | 75 | spu.setMarketPrice(skuCreateReqList.stream().map(ProductSkuCreateOrUpdateReqVO::getMarketPrice).max(Integer::compare).orElse(0)); |
| 68 | 76 | spu.setMaxPrice(skuCreateReqList.stream().map(ProductSkuCreateOrUpdateReqVO::getPrice).max(Integer::compare).orElse(0)); |
| 69 | 77 | spu.setMinPrice(skuCreateReqList.stream().map(ProductSkuCreateOrUpdateReqVO::getPrice).min(Integer::compare).orElse(0)); |
| 78 | + // TODO @luowenfeng:库存求和 | |
| 70 | 79 | ProductSpuMapper.insert(spu); |
| 80 | + | |
| 81 | + // 批量插入 SKU | |
| 82 | + // TODO @luowenfeng:convert 逻辑,交给 createProductSkus 一起处理 | |
| 71 | 83 | List<ProductSkuDO> skuDOList = ProductSkuConvert.INSTANCE.convertSkuDOList(skuCreateReqList); |
| 72 | 84 | skuDOList.forEach(v->v.setSpuId(spu.getId())); |
| 73 | - // 批量插入sku | |
| 74 | - productSkuService.createSkus(skuDOList); | |
| 85 | + productSkuService.createProductSkus(skuDOList); | |
| 75 | 86 | // 返回 |
| 76 | 87 | return spu.getId(); |
| 77 | 88 | } |
| 78 | 89 | |
| 79 | 90 | @Override |
| 80 | 91 | @Transactional |
| 81 | - public void updateSpu(ProductSpuUpdateReqVO updateReqVO) { | |
| 82 | - // 校验 spu 是否存在 | |
| 83 | - this.validateSpuExists(updateReqVO.getId()); | |
| 92 | + public void updateProductSpu(ProductSpuUpdateReqVO updateReqVO) { | |
| 93 | + // 校验 SPU 是否存在 | |
| 94 | + validateSpuExists(updateReqVO.getId()); | |
| 84 | 95 | // 校验分类 |
| 85 | 96 | categoryService.validateProductCategory(updateReqVO.getCategoryId()); |
| 97 | + // TODO @luowenfeng:校验品牌 | |
| 86 | 98 | // 校验SKU |
| 87 | 99 | List<ProductSkuCreateOrUpdateReqVO> skuCreateReqList = updateReqVO.getSkus(); |
| 88 | 100 | // 多规格才需校验 |
| 101 | + // TODO @luowenfeng:可以把 type 传递到 productSkuService 里,通过它统一判断处理 | |
| 89 | 102 | if(updateReqVO.getSpecType().equals(ProductSpuSpecTypeEnum.DISABLE.getType())) { |
| 90 | - productSkuService.validateSkus(skuCreateReqList); | |
| 103 | + productSkuService.validateProductSkus(skuCreateReqList); | |
| 91 | 104 | } |
| 92 | - // 更新 | |
| 105 | + | |
| 106 | + // 更新 SPU | |
| 93 | 107 | ProductSpuDO updateObj = ProductSpuConvert.INSTANCE.convert(updateReqVO); |
| 108 | + // TODO @计算各种字段 | |
| 94 | 109 | ProductSpuMapper.updateById(updateObj); |
| 95 | - // 更新 sku | |
| 96 | - productSkuService.updateSkus(updateObj.getId(), updateReqVO.getSkus()); | |
| 110 | + | |
| 111 | + // 更新 SKU | |
| 112 | + productSkuService.updateProductSkus(updateObj.getId(), updateReqVO.getSkus()); | |
| 97 | 113 | } |
| 98 | 114 | |
| 99 | 115 | @Override | ... | ... |
yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImplTest.java
| ... | ... | @@ -41,7 +41,7 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest { |
| 41 | 41 | ProductSpuCreateReqVO reqVO = randomPojo(ProductSpuCreateReqVO.class); |
| 42 | 42 | |
| 43 | 43 | // 调用 |
| 44 | - Long spuId = spuService.createSpu(reqVO); | |
| 44 | + Long spuId = spuService.createProductSpu(reqVO); | |
| 45 | 45 | // 断言 |
| 46 | 46 | assertNotNull(spuId); |
| 47 | 47 | // 校验记录的属性是否正确 |
| ... | ... | @@ -60,7 +60,7 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest { |
| 60 | 60 | }); |
| 61 | 61 | |
| 62 | 62 | // 调用 |
| 63 | - spuService.updateSpu(reqVO); | |
| 63 | + spuService.updateProductSpu(reqVO); | |
| 64 | 64 | // 校验是否更新正确 |
| 65 | 65 | ProductSpuDO spu = ProductSpuMapper.selectById(reqVO.getId()); // 获取最新的 |
| 66 | 66 | assertPojoEquals(reqVO, spu); |
| ... | ... | @@ -72,7 +72,7 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest { |
| 72 | 72 | ProductSpuUpdateReqVO reqVO = randomPojo(ProductSpuUpdateReqVO.class); |
| 73 | 73 | |
| 74 | 74 | // 调用, 并断言异常 |
| 75 | - assertServiceException(() -> spuService.updateSpu(reqVO), SPU_NOT_EXISTS); | |
| 75 | + assertServiceException(() -> spuService.updateProductSpu(reqVO), SPU_NOT_EXISTS); | |
| 76 | 76 | } |
| 77 | 77 | |
| 78 | 78 | @Test | ... | ... |