mall:code review 商品 spu 的后端代码

pull/2/head
YunaiV 2022-08-23 23:58:22 +08:00
parent edfe379848
commit c2fd24f597
8 changed files with 74 additions and 49 deletions

View File

@ -21,7 +21,9 @@ public class ProductSkuBaseVO {
@NotEmpty(message = "商品 SKU 名字不能为空") @NotEmpty(message = "商品 SKU 名字不能为空")
private String name; private String name;
@ApiModelProperty(value = "规格值数组-json格式 单规格中无此列表, [{propertyId: , valueId: }, {propertyId: , valueId: }]") /**
*
*/
private List<Property> properties; private List<Property> properties;
@ApiModelProperty(value = "销售价格,单位:分", required = true, example = "1024", notes = "单位:分") @ApiModelProperty(value = "销售价格,单位:分", required = true, example = "1024", notes = "单位:分")

View File

@ -35,15 +35,15 @@ public class ProductSpuController {
@PostMapping("/create") @PostMapping("/create")
@ApiOperation("创建商品 SPU") @ApiOperation("创建商品 SPU")
@PreAuthorize("@ss.hasPermission('product:spu:create')") @PreAuthorize("@ss.hasPermission('product:spu:create')")
public CommonResult<Long> createSpu(@Valid @RequestBody ProductSpuCreateReqVO createReqVO) { public CommonResult<Long> createProductSpu(@Valid @RequestBody ProductSpuCreateReqVO createReqVO) {
return success(spuService.createSpu(createReqVO)); return success(spuService.createProductSpu(createReqVO));
} }
@PutMapping("/update") @PutMapping("/update")
@ApiOperation("更新商品 SPU") @ApiOperation("更新商品 SPU")
@PreAuthorize("@ss.hasPermission('product:spu:update')") @PreAuthorize("@ss.hasPermission('product:spu:update')")
public CommonResult<Boolean> updateSpu(@Valid @RequestBody ProductSpuUpdateReqVO updateReqVO) { public CommonResult<Boolean> updateSpu(@Valid @RequestBody ProductSpuUpdateReqVO updateReqVO) {
spuService.updateSpu(updateReqVO); spuService.updateProductSpu(updateReqVO);
return success(true); return success(true);
} }

View File

@ -44,4 +44,5 @@ public interface ProductSkuMapper extends BaseMapperX<ProductSkuDO> {
.eqIfPresent(ProductSkuDO::getSpuId, spuId); .eqIfPresent(ProductSkuDO::getSpuId, spuId);
delete(lambdaQueryWrapperX); delete(lambdaQueryWrapperX);
} }
} }

View File

@ -68,14 +68,22 @@ public interface ProductSkuService {
* *
* @param list sku * @param list sku
*/ */
void validateSkus(List<ProductSkuCreateOrUpdateReqVO> list); void validateProductSkus(List<ProductSkuCreateOrUpdateReqVO> list);
/** /**
* sku * SKU
* *
* @param list sku * @param list SKU
*/ */
void createSkus(List<ProductSkuDO> list); void createProductSkus(List<ProductSkuDO> list);
/**
* SPU SKU
*
* @param spuId SPU
* @param skus SKU
*/
void updateProductSkus(Long spuId, List<ProductSkuCreateOrUpdateReqVO> skus);
/** /**
* sku * sku
@ -100,11 +108,4 @@ public interface ProductSkuService {
*/ */
void deleteSkuBySpuId(Long spuId); void deleteSkuBySpuId(Long spuId);
/**
* spuId spu sku
*
* @param spuId spu
* @param skus sku
*/
void updateSkus(Long spuId, List<ProductSkuCreateOrUpdateReqVO> skus);
} }

View File

@ -1,6 +1,5 @@
package cn.iocoder.yudao.module.product.service.sku; package cn.iocoder.yudao.module.product.service.sku;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.product.controller.admin.property.vo.ProductPropertyRespVO; import cn.iocoder.yudao.module.product.controller.admin.property.vo.ProductPropertyRespVO;
import cn.iocoder.yudao.module.product.controller.admin.propertyvalue.vo.ProductPropertyValueRespVO; import cn.iocoder.yudao.module.product.controller.admin.propertyvalue.vo.ProductPropertyValueRespVO;
@ -87,25 +86,30 @@ public class ProductSkuServiceImpl implements ProductSkuService {
return productSkuMapper.selectPage(pageReqVO); return productSkuMapper.selectPage(pageReqVO);
} }
// TODO luowenfeng参考下 yudao-cloud 的 checkProductAttr 方法,重构下
@Override @Override
public void validateSkus(List<ProductSkuCreateOrUpdateReqVO> list) { public void validateProductSkus(List<ProductSkuCreateOrUpdateReqVO> list) {
List<ProductSkuBaseVO.Property> skuPropertyList = list.stream().flatMap(p -> Optional.of(p.getProperties()).orElse(new ArrayList<>()).stream()).collect(Collectors.toList()); List<ProductSkuBaseVO.Property> skuPropertyList = list.stream().flatMap(p -> Optional.of(p.getProperties()).orElse(new ArrayList<>()).stream()).collect(Collectors.toList());
// 校验规格属性以及规格值是否存在 // 校验规格属性存在
// TODO @luowenfeng使用 CollectionUtils.convert
List<Long> propertyIds = skuPropertyList.stream().map(ProductSkuBaseVO.Property::getPropertyId).collect(Collectors.toList()); List<Long> propertyIds = skuPropertyList.stream().map(ProductSkuBaseVO.Property::getPropertyId).collect(Collectors.toList());
List<ProductPropertyRespVO> propertyAndValueList = productPropertyService.selectByIds(propertyIds); List<ProductPropertyRespVO> propertyAndValueList = productPropertyService.selectByIds(propertyIds);
// TODO @luowenfeng校验数量一致
if (propertyAndValueList.isEmpty()) { if (propertyAndValueList.isEmpty()) {
throw ServiceExceptionUtil.exception(PROPERTY_NOT_EXISTS); throw exception(PROPERTY_NOT_EXISTS);
} }
// 校验规格属性值存在
// TODO @luowenfeng使用 CollectionUtils.convert
Map<Long, ProductPropertyRespVO> propertyMap = propertyAndValueList.stream().collect(Collectors.toMap(ProductPropertyRespVO::getId, p -> p)); Map<Long, ProductPropertyRespVO> propertyMap = propertyAndValueList.stream().collect(Collectors.toMap(ProductPropertyRespVO::getId, p -> p));
skuPropertyList.forEach(p -> { skuPropertyList.forEach(p -> {
ProductPropertyRespVO productPropertyRespVO = propertyMap.get(p.getPropertyId()); ProductPropertyRespVO productPropertyRespVO = propertyMap.get(p.getPropertyId());
// 如果对应的属性名不存在或属性名下的属性值集合为空,给出提示 // 如果对应的属性名不存在或属性名下的属性值集合为空,给出提示
if (null == productPropertyRespVO || productPropertyRespVO.getPropertyValueList().isEmpty()) { if (null == productPropertyRespVO || productPropertyRespVO.getPropertyValueList().isEmpty()) {
throw ServiceExceptionUtil.exception(PROPERTY_NOT_EXISTS); throw exception(PROPERTY_NOT_EXISTS);
} }
// 判断改属性名对应的属性值是否存在,不存在,给出提示 // 判断改属性名对应的属性值是否存在,不存在,给出提示
if (!productPropertyRespVO.getPropertyValueList().stream().map(ProductPropertyValueRespVO::getId).collect(Collectors.toSet()).contains(p.getValueId())) { if (!productPropertyRespVO.getPropertyValueList().stream().map(ProductPropertyValueRespVO::getId).collect(Collectors.toSet()).contains(p.getValueId())) {
throw ServiceExceptionUtil.exception(ErrorCodeConstants.PROPERTY_VALUE_NOT_EXISTS); throw exception(ErrorCodeConstants.PROPERTY_VALUE_NOT_EXISTS);
} }
}); });
// 校验是否有重复的sku组合 // 校验是否有重复的sku组合
@ -114,13 +118,13 @@ public class ProductSkuServiceImpl implements ProductSkuService {
skuProperties.forEach(p -> { skuProperties.forEach(p -> {
// 组合属性值id为 1~2~3.... 形式的字符串通过set的特性判断是否有重复的组合 // 组合属性值id为 1~2~3.... 形式的字符串通过set的特性判断是否有重复的组合
if (!skuPropertiesConvertSet.add(p.stream().map(pr -> String.valueOf(pr.getValueId())).sorted().collect(Collectors.joining("")))) { if (!skuPropertiesConvertSet.add(p.stream().map(pr -> String.valueOf(pr.getValueId())).sorted().collect(Collectors.joining("")))) {
throw ServiceExceptionUtil.exception(ErrorCodeConstants.SKU_PROPERTIES_DUPLICATED); throw exception(ErrorCodeConstants.SKU_PROPERTIES_DUPLICATED);
} }
}); });
} }
@Override @Override
public void createSkus(List<ProductSkuDO> skuDOList) { public void createProductSkus(List<ProductSkuDO> skuDOList) {
productSkuMapper.insertBatch(skuDOList); productSkuMapper.insertBatch(skuDOList);
} }
@ -141,21 +145,23 @@ public class ProductSkuServiceImpl implements ProductSkuService {
@Override @Override
@Transactional @Transactional
public void updateSkus(Long spuId, List<ProductSkuCreateOrUpdateReqVO> skus) { public void updateProductSkus(Long spuId, List<ProductSkuCreateOrUpdateReqVO> skus) {
List<ProductSkuDO> allUpdateSkus = ProductSkuConvert.INSTANCE.convertSkuDOList(skus);
// 查询 spu 下已经存在的 sku 的集合 // 查询 spu 下已经存在的 sku 的集合
// TODO @luowenfengselectListBySpuId 搞个
List<ProductSkuDO> existsSkus = productSkuMapper.selectBySpuIds(Collections.singletonList(spuId)); List<ProductSkuDO> existsSkus = productSkuMapper.selectBySpuIds(Collections.singletonList(spuId));
// TODO @franky使用 CollUtils 即可 // TODO @franky使用 CollUtils 即可
Map<Long, ProductSkuDO> existsSkuMap = existsSkus.stream().collect(Collectors.toMap(ProductSkuDO::getId, p -> p)); Map<Long, ProductSkuDO> existsSkuMap = existsSkus.stream().collect(Collectors.toMap(ProductSkuDO::getId, p -> p));
// 拆分三个集合, 新插入的, 需要更新的,需要删除的 // 拆分三个集合,新插入的、需要更新的、需要删除的
List<ProductSkuDO> insertSkus = new ArrayList<>(); List<ProductSkuDO> insertSkus = new ArrayList<>();
List<ProductSkuDO> updateSkus = new ArrayList<>(); List<ProductSkuDO> updateSkus = new ArrayList<>();
List<ProductSkuDO> deleteSkus = new ArrayList<>(); List<ProductSkuDO> deleteSkus = new ArrayList<>();
// TODO @芋艿:是不是基于规格匹配会比较好。 // TODO @芋艿:是不是基于规格匹配会比较好。
List<ProductSkuDO> allUpdateSkus = ProductSkuConvert.INSTANCE.convertSkuDOList(skus);
allUpdateSkus.forEach(p -> { allUpdateSkus.forEach(p -> {
if (null != p.getId()) { if (p.getId() != null) {
// TODO @luowenfengcontains
if (existsSkuMap.get(p.getId()) != null) { if (existsSkuMap.get(p.getId()) != null) {
updateSkus.add(p); updateSkus.add(p);
return; return;
@ -167,14 +173,13 @@ public class ProductSkuServiceImpl implements ProductSkuService {
insertSkus.add(p); insertSkus.add(p);
}); });
// TODO @luowenfeng使用 CollUtil.isNotEmpty 判断
if (insertSkus.size() > 0) { if (insertSkus.size() > 0) {
productSkuMapper.insertBatch(insertSkus); productSkuMapper.insertBatch(insertSkus);
} }
if (updateSkus.size() > 0) { if (updateSkus.size() > 0) {
updateSkus.forEach(p -> productSkuMapper.updateById(p)); updateSkus.forEach(p -> productSkuMapper.updateById(p));
} }
if (deleteSkus.size() > 0) { if (deleteSkus.size() > 0) {
productSkuMapper.deleteBatchIds(deleteSkus.stream().map(ProductSkuDO::getId).collect(Collectors.toList())); productSkuMapper.deleteBatchIds(deleteSkus.stream().map(ProductSkuDO::getId).collect(Collectors.toList()));
} }

View File

@ -21,19 +21,19 @@ import java.util.List;
public interface ProductSpuService { public interface ProductSpuService {
/** /**
* spu * SPU
* *
* @param createReqVO * @param createReqVO
* @return * @return
*/ */
Long createSpu(@Valid ProductSpuCreateReqVO createReqVO); Long createProductSpu(@Valid ProductSpuCreateReqVO createReqVO);
/** /**
* spu * SPU
* *
* @param updateReqVO * @param updateReqVO
*/ */
void updateSpu(@Valid ProductSpuUpdateReqVO updateReqVO); void updateProductSpu(@Valid ProductSpuUpdateReqVO updateReqVO);
/** /**
* spu * spu

View File

@ -7,7 +7,10 @@ import cn.iocoder.yudao.module.product.controller.admin.propertyvalue.vo.Product
import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuBaseVO; import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuBaseVO;
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.sku.vo.ProductSkuRespVO; import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuRespVO;
import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*; import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuCreateReqVO;
import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuUpdateReqVO;
import cn.iocoder.yudao.module.product.controller.admin.spu.vo.SpuPageReqVO;
import cn.iocoder.yudao.module.product.controller.admin.spu.vo.SpuRespVO;
import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageReqVO; import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageReqVO;
import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageRespVO; import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageRespVO;
import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert; import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert;
@ -53,47 +56,60 @@ public class ProductSpuServiceImpl implements ProductSpuService {
@Override @Override
@Transactional @Transactional
public Long createSpu(ProductSpuCreateReqVO createReqVO) { public Long createProductSpu(ProductSpuCreateReqVO createReqVO) {
// 校验分类 // 校验分类
// TODO @luowenfeng可以在这个类里加个方法校验分类商品必须挂在三级分类下
categoryService.validateProductCategory(createReqVO.getCategoryId()); categoryService.validateProductCategory(createReqVO.getCategoryId());
// TODO @luowenfeng校验品牌
// 校验SKU // 校验SKU
List<ProductSkuCreateOrUpdateReqVO> skuCreateReqList = createReqVO.getSkus(); List<ProductSkuCreateOrUpdateReqVO> skuCreateReqList = createReqVO.getSkus();
// 多规格才需校验 // 多规格才需校验
// TODO @luowenfeng可以把 type 传递到 productSkuService 里,通过它统一判断处理
if(Objects.equals(createReqVO.getSpecType(), ProductSpuSpecTypeEnum.DISABLE.getType())) { if(Objects.equals(createReqVO.getSpecType(), ProductSpuSpecTypeEnum.DISABLE.getType())) {
productSkuService.validateSkus(skuCreateReqList); productSkuService.validateProductSkus(skuCreateReqList);
} }
// 插入SPU
// 插入 SPU
ProductSpuDO spu = ProductSpuConvert.INSTANCE.convert(createReqVO); ProductSpuDO spu = ProductSpuConvert.INSTANCE.convert(createReqVO);
// TODO @luowenfeng可以在 CollectionUtils 增加 getMaxValue 方法,增加一个 defaultValue 方法,如果为空,则返回 defaultValue
spu.setMarketPrice(skuCreateReqList.stream().map(ProductSkuCreateOrUpdateReqVO::getMarketPrice).max(Integer::compare).orElse(0)); spu.setMarketPrice(skuCreateReqList.stream().map(ProductSkuCreateOrUpdateReqVO::getMarketPrice).max(Integer::compare).orElse(0));
spu.setMaxPrice(skuCreateReqList.stream().map(ProductSkuCreateOrUpdateReqVO::getPrice).max(Integer::compare).orElse(0)); spu.setMaxPrice(skuCreateReqList.stream().map(ProductSkuCreateOrUpdateReqVO::getPrice).max(Integer::compare).orElse(0));
spu.setMinPrice(skuCreateReqList.stream().map(ProductSkuCreateOrUpdateReqVO::getPrice).min(Integer::compare).orElse(0)); spu.setMinPrice(skuCreateReqList.stream().map(ProductSkuCreateOrUpdateReqVO::getPrice).min(Integer::compare).orElse(0));
// TODO @luowenfeng库存求和
ProductSpuMapper.insert(spu); ProductSpuMapper.insert(spu);
// 批量插入 SKU
// TODO @luowenfengconvert 逻辑,交给 createProductSkus 一起处理
List<ProductSkuDO> skuDOList = ProductSkuConvert.INSTANCE.convertSkuDOList(skuCreateReqList); List<ProductSkuDO> skuDOList = ProductSkuConvert.INSTANCE.convertSkuDOList(skuCreateReqList);
skuDOList.forEach(v->v.setSpuId(spu.getId())); skuDOList.forEach(v->v.setSpuId(spu.getId()));
// 批量插入sku productSkuService.createProductSkus(skuDOList);
productSkuService.createSkus(skuDOList);
// 返回 // 返回
return spu.getId(); return spu.getId();
} }
@Override @Override
@Transactional @Transactional
public void updateSpu(ProductSpuUpdateReqVO updateReqVO) { public void updateProductSpu(ProductSpuUpdateReqVO updateReqVO) {
// 校验 spu 是否存在 // 校验 SPU 是否存在
this.validateSpuExists(updateReqVO.getId()); validateSpuExists(updateReqVO.getId());
// 校验分类 // 校验分类
categoryService.validateProductCategory(updateReqVO.getCategoryId()); categoryService.validateProductCategory(updateReqVO.getCategoryId());
// TODO @luowenfeng校验品牌
// 校验SKU // 校验SKU
List<ProductSkuCreateOrUpdateReqVO> skuCreateReqList = updateReqVO.getSkus(); List<ProductSkuCreateOrUpdateReqVO> skuCreateReqList = updateReqVO.getSkus();
// 多规格才需校验 // 多规格才需校验
// TODO @luowenfeng可以把 type 传递到 productSkuService 里,通过它统一判断处理
if(updateReqVO.getSpecType().equals(ProductSpuSpecTypeEnum.DISABLE.getType())) { if(updateReqVO.getSpecType().equals(ProductSpuSpecTypeEnum.DISABLE.getType())) {
productSkuService.validateSkus(skuCreateReqList); productSkuService.validateProductSkus(skuCreateReqList);
} }
// 更新
// 更新 SPU
ProductSpuDO updateObj = ProductSpuConvert.INSTANCE.convert(updateReqVO); ProductSpuDO updateObj = ProductSpuConvert.INSTANCE.convert(updateReqVO);
// TODO @计算各种字段
ProductSpuMapper.updateById(updateObj); ProductSpuMapper.updateById(updateObj);
// 更新 sku
productSkuService.updateSkus(updateObj.getId(), updateReqVO.getSkus()); // 更新 SKU
productSkuService.updateProductSkus(updateObj.getId(), updateReqVO.getSkus());
} }
@Override @Override

View File

@ -41,7 +41,7 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest {
ProductSpuCreateReqVO reqVO = randomPojo(ProductSpuCreateReqVO.class); ProductSpuCreateReqVO reqVO = randomPojo(ProductSpuCreateReqVO.class);
// 调用 // 调用
Long spuId = spuService.createSpu(reqVO); Long spuId = spuService.createProductSpu(reqVO);
// 断言 // 断言
assertNotNull(spuId); assertNotNull(spuId);
// 校验记录的属性是否正确 // 校验记录的属性是否正确
@ -60,7 +60,7 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest {
}); });
// 调用 // 调用
spuService.updateSpu(reqVO); spuService.updateProductSpu(reqVO);
// 校验是否更新正确 // 校验是否更新正确
ProductSpuDO spu = ProductSpuMapper.selectById(reqVO.getId()); // 获取最新的 ProductSpuDO spu = ProductSpuMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, spu); assertPojoEquals(reqVO, spu);
@ -72,7 +72,7 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest {
ProductSpuUpdateReqVO reqVO = randomPojo(ProductSpuUpdateReqVO.class); ProductSpuUpdateReqVO reqVO = randomPojo(ProductSpuUpdateReqVO.class);
// 调用, 并断言异常 // 调用, 并断言异常
assertServiceException(() -> spuService.updateSpu(reqVO), SPU_NOT_EXISTS); assertServiceException(() -> spuService.updateProductSpu(reqVO), SPU_NOT_EXISTS);
} }
@Test @Test