浏览代码

feature(单元测试): ProductSpuServiceImpl单元测试

luowenfeng 2 年之前
父节点
当前提交
aea763e96e

+ 1 - 1
sql/optional/mall/mall.sql

@@ -248,7 +248,7 @@ DROP TABLE IF EXISTS `product_spu`;
 CREATE TABLE `product_spu` (
                                `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
                                `tenant_id` bigint NOT NULL DEFAULT '0' COMMENT '租户编号',
-                               `brand_id` int DEFAULT NULL COMMENT '商品品牌编号',
+                               `brand_id` bigint DEFAULT NULL COMMENT '商品品牌编号',
                                `category_id` bigint NOT NULL COMMENT '分类id',
                                `spec_type` int NOT NULL COMMENT '规格类型:0 单规格 1 多规格',
                                `code` varchar(128)  DEFAULT NULL COMMENT '商品编码',

+ 2 - 2
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/spu/ProductSpuStatusEnum.java

@@ -19,12 +19,12 @@ public enum ProductSpuStatusEnum implements IntArrayValuable {
     DISABLE(0, "下架"),
     ENABLE(1, "上架"),;
 
-    public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ProductSpuStatusEnum::getStyle).toArray();
+    public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ProductSpuStatusEnum::getStatus).toArray();
 
     /**
      * 状态
      */
-    private final Integer style;
+    private final Integer status;
     /**
      * 状态名
      */

+ 1 - 0
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/sku/ProductSkuDO.java

@@ -97,6 +97,7 @@ public class ProductSkuDO extends BaseDO {
      * 商品属性
      */
     @Data
+    @AllArgsConstructor
     public static class Property {
 
         /**

+ 15 - 25
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java

@@ -28,7 +28,10 @@ import org.springframework.transaction.annotation.Transactional;
 import org.springframework.validation.annotation.Validated;
 
 import javax.annotation.Resource;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
 import java.util.stream.Collectors;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -44,7 +47,7 @@ import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_E
 public class ProductSpuServiceImpl implements ProductSpuService {
 
     @Resource
-    private ProductSpuMapper ProductSpuMapper;
+    private ProductSpuMapper productSpuMapper;
 
     @Resource
     private ProductCategoryService categoryService;
@@ -77,7 +80,7 @@ public class ProductSpuServiceImpl implements ProductSpuService {
         spu.setMaxPrice(CollectionUtils.getMaxValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getPrice));
         spu.setMinPrice(CollectionUtils.getMinValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getPrice));
         spu.setTotalStock(CollectionUtils.getSumValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getStock, Integer::sum));
-        ProductSpuMapper.insert(spu);
+        productSpuMapper.insert(spu);
         // 插入 SKU
         productSkuService.createSkus(spu.getId(), skuCreateReqList);
         // 返回
@@ -95,7 +98,6 @@ public class ProductSpuServiceImpl implements ProductSpuService {
         brandService.validateProductBrand(updateReqVO.getBrandId());
         // 校验SKU
         List<ProductSkuCreateOrUpdateReqVO> skuCreateReqList = updateReqVO.getSkus();
-        // 多规格才需校验
         productSkuService.validateSkus(skuCreateReqList, updateReqVO.getSpecType());
 
         // 更新 SPU
@@ -104,7 +106,7 @@ public class ProductSpuServiceImpl implements ProductSpuService {
         updateObj.setMaxPrice(CollectionUtils.getMaxValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getPrice));
         updateObj.setMinPrice(CollectionUtils.getMinValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getPrice));
         updateObj.setTotalStock(CollectionUtils.getSumValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getStock, Integer::sum));
-        ProductSpuMapper.updateById(updateObj);
+        productSpuMapper.updateById(updateObj);
         // 批量更新 SKU
         productSkuService.updateProductSkus(updateObj.getId(), updateReqVO.getSkus());
     }
@@ -115,13 +117,13 @@ public class ProductSpuServiceImpl implements ProductSpuService {
         // 校验存在
         validateSpuExists(id);
         // 删除 SPU
-        ProductSpuMapper.deleteById(id);
+        productSpuMapper.deleteById(id);
         // 删除关联的 SKU
         productSkuService.deleteSkuBySpuId(id);
     }
 
     private void validateSpuExists(Long id) {
-        if (ProductSpuMapper.selectById(id) == null) {
+        if (productSpuMapper.selectById(id) == null) {
             throw exception(SPU_NOT_EXISTS);
         }
     }
@@ -129,7 +131,7 @@ public class ProductSpuServiceImpl implements ProductSpuService {
     @Override
     // TODO @芋艿:需要再 review 下
     public ProductSpuDetailRespVO getSpuDetail(Long id) {
-        ProductSpuDO spu = ProductSpuMapper.selectById(id);
+        ProductSpuDO spu = productSpuMapper.selectById(id);
         ProductSpuDetailRespVO respVO = BeanUtil.copyProperties(spu, ProductSpuDetailRespVO.class);
         if (null != spu) {
             List<ProductSpuDetailRespVO.Sku> skuReqs = ProductSkuConvert.INSTANCE.convertList03(productSkuService.getSkusBySpuId(id));
@@ -162,19 +164,6 @@ public class ProductSpuServiceImpl implements ProductSpuService {
                 });
                 respVO.setProductPropertyViews(productPropertyViews);
             }
-            // 组合分类
-//            if (null != respVO.getCategoryId()) {
-//                LinkedList<Long> categoryArray = new LinkedList<>();
-//                Long parentId = respVO.getCategoryId();
-//                categoryArray.addFirst(parentId);
-//                while (parentId != 0) {
-//                    parentId = categoryService.getCategory(parentId).getParentId();
-//                    if (parentId > 0) {
-//                        categoryArray.addFirst(parentId);
-//                    }
-//                }
-//
-//            }
             respVO.setCategoryIds(respVO.getCategoryId());
         }
         return respVO;
@@ -182,12 +171,12 @@ public class ProductSpuServiceImpl implements ProductSpuService {
 
     @Override
     public ProductSpuRespVO getSpu(Long id) {
-        return ProductSpuConvert.INSTANCE.convert(ProductSpuMapper.selectById(id));
+        return ProductSpuConvert.INSTANCE.convert(productSpuMapper.selectById(id));
     }
 
     @Override
     public List<ProductSpuDO> getSpuList(Collection<Long> ids) {
-        return ProductSpuMapper.selectBatchIds(ids);
+        return productSpuMapper.selectBatchIds(ids);
     }
 
     @Override
@@ -199,13 +188,14 @@ public class ProductSpuServiceImpl implements ProductSpuService {
                 remindSpuIds.add(null);
             }
         }
-        return ProductSpuConvert.INSTANCE.convertPage(ProductSpuMapper.selectPage(pageReqVO, remindSpuIds));
+        return ProductSpuConvert.INSTANCE.convertPage(productSpuMapper.selectPage(pageReqVO, remindSpuIds));
     }
 
     @Override
     public PageResult<AppSpuPageRespVO> getSpuPage(AppSpuPageReqVO pageReqVO) {
-        PageResult<ProductSpuDO> productSpuDOPageResult = ProductSpuMapper.selectPage(ProductSpuConvert.INSTANCE.convert(pageReqVO));
+        PageResult<ProductSpuDO> productSpuDOPageResult = productSpuMapper.selectPage(ProductSpuConvert.INSTANCE.convert(pageReqVO));
         PageResult<AppSpuPageRespVO> pageResult = new PageResult<>();
+        // TODO @芋艿 这里用convert如何解决
         List<AppSpuPageRespVO> collect = productSpuDOPageResult.getList()
                 .stream()
                 .map(ProductSpuConvert.INSTANCE::convertAppResp)

+ 243 - 105
yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImplTest.java

@@ -1,163 +1,301 @@
 package cn.iocoder.yudao.module.product.service.spu;
 
+import cn.iocoder.yudao.framework.common.exception.ServiceException;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
-import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuCreateReqVO;
-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.admin.property.vo.property.ProductPropertyRespVO;
+import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueRespVO;
+import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO;
+import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*;
+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.convert.spu.ProductSpuConvert;
+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.mysql.spu.ProductSpuMapper;
-import org.junit.jupiter.api.Disabled;
+import cn.iocoder.yudao.module.product.enums.spu.ProductSpuSpecTypeEnum;
+import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
+import cn.iocoder.yudao.module.product.service.brand.ProductBrandServiceImpl;
+import cn.iocoder.yudao.module.product.service.category.ProductCategoryServiceImpl;
+import cn.iocoder.yudao.module.product.service.property.ProductPropertyService;
+import cn.iocoder.yudao.module.product.service.property.ProductPropertyValueService;
+import cn.iocoder.yudao.module.product.service.sku.ProductSkuServiceImpl;
+import com.google.common.collect.Lists;
+import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import org.springframework.boot.test.mock.mockito.MockBean;
 import org.springframework.context.annotation.Import;
 
 import javax.annotation.Resource;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
-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.assertServiceException;
 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
-import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_EXISTS;
-import static org.junit.jupiter.api.Assertions.*;
 
 /**
-* {@link ProductSpuServiceImpl} 的单元测试类
-*
-* @author 芋道源码
-*/
+ * {@link ProductSpuServiceImpl} 的单元测试类
+ *
+ * @author 芋道源码
+ */
 @Import(ProductSpuServiceImpl.class)
 public class ProductSpuServiceImplTest extends BaseDbUnitTest {
 
     @Resource
-    private ProductSpuServiceImpl spuService;
+    private ProductSpuServiceImpl productSpuService;
 
     @Resource
-    private ProductSpuMapper ProductSpuMapper;
+    private ProductSpuMapper productSpuMapper;
+
+
+    @MockBean
+    private ProductSkuServiceImpl productSkuService;
+
+    @MockBean
+    private ProductCategoryServiceImpl categoryService;
+
+    @MockBean
+    private ProductBrandServiceImpl brandService;
+
+    @MockBean
+    private ProductPropertyService productPropertyService;
+
+    @MockBean
+    private ProductPropertyValueService productPropertyValueService;
+
 
     @Test
     public void testCreateSpu_success() {
         // 准备参数
-        ProductSpuCreateReqVO reqVO = randomPojo(ProductSpuCreateReqVO.class);
+        ProductSpuCreateReqVO createReqVO = randomPojo(ProductSpuCreateReqVO.class, o -> {
+            o.setSpecType(ProductSpuSpecTypeEnum.DISABLE.getType());
+            o.setStatus(ProductSpuStatusEnum.ENABLE.getStatus());
+        });
+
+        // 校验SKU
+        List<ProductSkuCreateOrUpdateReqVO> skuCreateReqList = createReqVO.getSkus();
+
+        Long spu = productSpuService.createSpu(createReqVO);
+        ProductSpuDO productSpuDO = productSpuMapper.selectById(spu);
+
+        createReqVO.setMarketPrice(CollectionUtils.getMaxValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getMarketPrice));
+        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);
 
-        // 调用
-        Long spuId = spuService.createSpu(reqVO);
-        // 断言
-        assertNotNull(spuId);
-        // 校验记录的属性是否正确
-        ProductSpuDO spu = ProductSpuMapper.selectById(spuId);
-        assertPojoEquals(reqVO, spu);
     }
 
     @Test
     public void testUpdateSpu_success() {
-        // mock 数据
-        ProductSpuDO dbSpu = randomPojo(ProductSpuDO.class);
-        ProductSpuMapper.insert(dbSpu);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        ProductSpuDO createReqVO = randomPojo(ProductSpuDO.class);
+        productSpuMapper.insert(createReqVO);
         // 准备参数
         ProductSpuUpdateReqVO reqVO = randomPojo(ProductSpuUpdateReqVO.class, o -> {
-            o.setId(dbSpu.getId()); // 设置更新的 ID
+            o.setId(createReqVO.getId()); // 设置更新的 ID
+            o.setSpecType(ProductSpuSpecTypeEnum.DISABLE.getType());
+            o.setStatus(ProductSpuStatusEnum.DISABLE.getStatus());
         });
-
         // 调用
-        spuService.updateSpu(reqVO);
+        productSpuService.updateSpu(reqVO);
+
+        List<ProductSkuCreateOrUpdateReqVO> skuCreateReqList = reqVO.getSkus();
+        reqVO.setMarketPrice(CollectionUtils.getMaxValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getMarketPrice));
+        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);
     }
 
     @Test
-    public void testUpdateSpu_notExists() {
+    public void testValidateSpuExists_exception() {
+        ProductSpuUpdateReqVO reqVO = randomPojo(ProductSpuUpdateReqVO.class, o -> {
+            o.setSpecType(ProductSpuSpecTypeEnum.DISABLE.getType());
+            o.setStatus(ProductSpuStatusEnum.DISABLE.getStatus());
+        });
+        // 调用
+        Assertions.assertThrows(ServiceException.class, () -> productSpuService.updateSpu(reqVO));
+    }
+
+    @Test
+    void deleteSpu() {
+        // 准备参数
+        ProductSpuDO createReqVO = randomPojo(ProductSpuDO.class);
+        productSpuMapper.insert(createReqVO);
+
+        // 调用
+        productSpuService.deleteSpu(createReqVO.getId());
+
+        Assertions.assertNull(productSpuMapper.selectById(createReqVO.getId()));
+    }
+
+    @Test
+    void getSpuDetail() {
+        // 准备spu参数
+        ProductSpuDO createReqVO = randomPojo(ProductSpuDO.class, o -> {
+            o.setSpecType(ProductSpuSpecTypeEnum.DISABLE.getType());
+        });
+        productSpuMapper.insert(createReqVO);
+
+        // 创建两个属性
+        ArrayList<ProductPropertyRespVO> productPropertyRespVOS = Lists.newArrayList(
+                randomPojo(ProductPropertyRespVO.class),
+                randomPojo(ProductPropertyRespVO.class));
+
+        // 所有属性值
+        ArrayList<ProductPropertyValueRespVO> productPropertyValueRespVO = new ArrayList<>();
+
+        // 每个属性创建属性值
+        productPropertyRespVOS.forEach(v -> {
+            ProductPropertyValueRespVO productPropertyValueRespVO1 = randomPojo(ProductPropertyValueRespVO.class, o -> o.setPropertyId(v.getId()));
+            productPropertyValueRespVO.add(productPropertyValueRespVO1);
+        });
+
+        // 属性值建立笛卡尔积
+        Map<Long, List<ProductPropertyValueRespVO>> collect = productPropertyValueRespVO.stream().collect(Collectors.groupingBy(ProductPropertyValueRespVO::getPropertyId));
+        List<List<ProductPropertyValueRespVO>> lists = cartesianProduct(Lists.newArrayList(collect.values()));
+
+        // 准备sku参数
+        ArrayList<ProductSkuDO> productSkuDOS = Lists.newArrayList();
+        lists.forEach(pp -> {
+            List<ProductSkuDO.Property> property = pp.stream().map(ppv -> new ProductSkuDO.Property(ppv.getPropertyId(), ppv.getId())).collect(Collectors.toList());
+            ProductSkuDO productSkuDO = randomPojo(ProductSkuDO.class, o -> {
+                o.setProperties(property);
+            });
+            productSkuDOS.add(productSkuDO);
+
+        });
+
+        Mockito.when(productSkuService.getSkusBySpuId(createReqVO.getId())).thenReturn(productSkuDOS);
+        Mockito.when(productPropertyValueService.getPropertyValueListByPropertyId(new ArrayList<>(collect.keySet()))).thenReturn(productPropertyValueRespVO);
+        Mockito.when(productPropertyService.getPropertyList(new ArrayList<>(collect.keySet()))).thenReturn(productPropertyRespVOS);
+
+        // 调用
+        ProductSpuDetailRespVO spuDetail = productSpuService.getSpuDetail(createReqVO.getId());
+
+        assertPojoEquals(createReqVO, spuDetail);
+    }
+
+    @Test
+    void getSpu() {
         // 准备参数
-        ProductSpuUpdateReqVO reqVO = randomPojo(ProductSpuUpdateReqVO.class);
+        ProductSpuDO createReqVO = randomPojo(ProductSpuDO.class);
+        productSpuMapper.insert(createReqVO);
 
-        // 调用, 并断言异常
-        assertServiceException(() -> spuService.updateSpu(reqVO), SPU_NOT_EXISTS);
+        ProductSpuRespVO spu = productSpuService.getSpu(createReqVO.getId());
+        assertPojoEquals(createReqVO, spu);
     }
 
     @Test
-    public void testDeleteSpu_success() {
-        // mock 数据
-        ProductSpuDO dbSpu = randomPojo(ProductSpuDO.class);
-        ProductSpuMapper.insert(dbSpu);// @Sql: 先插入出一条存在的数据
+    void getSpuList() {
         // 准备参数
-        Long id = dbSpu.getId();
+        ArrayList<ProductSpuDO> createReqVO = Lists.newArrayList(randomPojo(ProductSpuDO.class), randomPojo(ProductSpuDO.class));
+        productSpuMapper.insertBatch(createReqVO);
 
         // 调用
-        spuService.deleteSpu(id);
-       // 校验数据不存在了
-       assertNull(ProductSpuMapper.selectById(id));
+        List<ProductSpuDO> spuList = productSpuService.getSpuList(createReqVO.stream().map(ProductSpuDO::getId).collect(Collectors.toList()));
+        Assertions.assertIterableEquals(createReqVO, spuList);
     }
 
     @Test
-    public void testDeleteSpu_notExists() {
+    void getSpuPage() {
         // 准备参数
-        Long id = 1L;
+        ProductSpuDO createReqVO = randomPojo(ProductSpuDO.class);
+        productSpuMapper.insert(createReqVO);
+
+        ArrayList<ProductSkuDO> remindSpuIds = Lists.newArrayList(
+//                randomPojo(ProductSkuDO.class, o -> o.setSpuId(createReqVO.getId())),
+//                randomPojo(ProductSkuDO.class, o -> o.setSpuId(createReqVO.getId()))
+        );
 
-        // 调用, 并断言异常
-        assertServiceException(() -> spuService.deleteSpu(id), SPU_NOT_EXISTS);
+        Mockito.when(productSkuService.getRemindSpuIds()).thenReturn(remindSpuIds);
+
+        // 调用
+        ProductSpuPageReqVO productSpuPageReqVO = new ProductSpuPageReqVO();
+        productSpuPageReqVO.setTabStatus(2);
+
+        PageResult<ProductSpuRespVO> spuPage = productSpuService.getSpuPage(productSpuPageReqVO);
+
+        ArrayList<Long> resultRemindSpuIds = new ArrayList<>();
+        resultRemindSpuIds.add(null);
+        PageResult<ProductSpuRespVO> result = ProductSpuConvert.INSTANCE.convertPage(productSpuMapper.selectPage(productSpuPageReqVO, resultRemindSpuIds));
+        Assertions.assertIterableEquals(result.getList(), spuPage.getList());
+        Assertions.assertEquals(spuPage.getTotal(), result.getTotal());
     }
 
     @Test
-    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
-    public void testGetSpuPage() {
-       // mock 数据
-       ProductSpuDO dbSpu = randomPojo(ProductSpuDO.class, o -> { // 等会查询到
-           o.setName(null);
-           o.setSellPoint(null);
-           o.setDescription(null);
-           o.setCategoryId(null);
-           o.setPicUrls(null);
-           o.setSort(null);
-//           o.setLikeCount(null);
-//           o.setPrice(null);
-//           o.setQuantity(null);
-           o.setStatus(null);
-           o.setCreateTime(null);
-       });
-       ProductSpuMapper.insert(dbSpu);
-       // 测试 name 不匹配
-       ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setName(null)));
-       // 测试 sellPoint 不匹配
-       ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setSellPoint(null)));
-       // 测试 description 不匹配
-       ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setDescription(null)));
-       // 测试 categoryId 不匹配
-       ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setCategoryId(null)));
-       // 测试 picUrls 不匹配
-       ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setPicUrls(null)));
-       // 测试 sort 不匹配
-       ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setSort(null)));
-       // 测试 likeCount 不匹配
-//       ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setLikeCount(null)));
-       // 测试 price 不匹配
-//       ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setPrice(null)));
-       // 测试 quantity 不匹配
-//       ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setQuantity(null)));
-       // 测试 status 不匹配
-       ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setStatus(null)));
-       // 测试 createTime 不匹配
-       ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setCreateTime(null)));
-       // 准备参数
-       ProductSpuPageReqVO reqVO = new ProductSpuPageReqVO();
-       reqVO.setName(null);
-       reqVO.setSellPoint(null);
-       reqVO.setDescription(null);
-       reqVO.setCategoryId(null);
-       reqVO.setPicUrls(null);
-       reqVO.setSort(null);
-       reqVO.setLikeCount(null);
-       reqVO.setPrice(null);
-       reqVO.setQuantity(null);
-       reqVO.setStatus(null);
-       reqVO.setCreateTime(null);
-
-       // 调用
-       PageResult<ProductSpuRespVO> pageResult = spuService.getSpuPage(reqVO);
-       // 断言
-       assertEquals(1, pageResult.getTotal());
-       assertEquals(1, pageResult.getList().size());
-       assertPojoEquals(dbSpu, pageResult.getList().get(0));
+    void testGetSpuPage() {
+// 准备参数
+        ProductSpuDO createReqVO = randomPojo(ProductSpuDO.class, o -> {
+            o.setCategoryId(2L);
+        });
+        productSpuMapper.insert(createReqVO);
+
+        ArrayList<ProductSkuDO> remindSpuIds = Lists.newArrayList(
+//                randomPojo(ProductSkuDO.class, o -> o.setSpuId(createReqVO.getId())),
+//                randomPojo(ProductSkuDO.class, o -> o.setSpuId(createReqVO.getId()))
+        );
+
+        Mockito.when(productSkuService.getRemindSpuIds()).thenReturn(remindSpuIds);
+
+        // 调用
+        AppSpuPageReqVO appSpuPageReqVO = new AppSpuPageReqVO();
+        appSpuPageReqVO.setCategoryId(2L);
+
+        PageResult<AppSpuPageRespVO> spuPage = productSpuService.getSpuPage(appSpuPageReqVO);
+
+        PageResult<ProductSpuDO> result = productSpuMapper.selectPage(
+                ProductSpuConvert.INSTANCE.convert(appSpuPageReqVO));
+
+        List<AppSpuPageRespVO> collect = result.getList()
+                .stream()
+                .map(ProductSpuConvert.INSTANCE::convertAppResp)
+                .collect(Collectors.toList());
+
+        Assertions.assertIterableEquals(collect, spuPage.getList());
+        Assertions.assertEquals(spuPage.getTotal(), result.getTotal());
+    }
+
+
+    /**
+     * 生成笛卡尔积
+     *
+     * @param data 数据
+     * @return 笛卡尔积
+     */
+    public static <T> List<List<T>> cartesianProduct(List<List<T>> data) {
+        List<List<T>> res = null; // 结果集(当前为第N个List,则该处存放的就为前N-1个List的笛卡尔积集合)
+        for (List<T> list : data) { // 遍历数据
+            List<List<T>> temp = new ArrayList<>(); // 临时结果集,存放本次循环后生成的笛卡尔积集合
+            if (res == null) { // 结果集为null表示第一次循环既list为第一个List
+                for (T t : list) { // 便利第一个List
+                    // 利用stream生成List,第一个List的笛卡尔积集合约等于自己本身(需要创建一个List并把对象添加到当中),存放到临时结果集
+                    temp.add(Stream.of(t).collect(Collectors.toList()));
+                }
+                res = temp; // 将临时结果集赋值给结果集
+                continue; // 跳过本次循环
+            }
+            // 不为第一个List,计算前面的集合(笛卡尔积)和当前List的笛卡尔积集合
+            for (T t : list) { // 便利
+                for (List<T> rl : res) { // 便利前面的笛卡尔积集合
+                    // 利用stream生成List
+                    temp.add(Stream.concat(rl.stream(), Stream.of(t)).collect(Collectors.toList()));
+                }
+            }
+            res = temp; // 将临时结果集赋值给结果集
+        }
+        // 返回结果
+        return res;
     }
 
 }

+ 2 - 2
yudao-module-mall/yudao-module-product-biz/src/test/resources/sql/clean.sql

@@ -1,3 +1,3 @@
-DELETE FROM "product_category";
+DELETE FROM "product_sku";
 
-DELETE FROM "product_brand";
+DELETE FROM "product_spu";

+ 53 - 29
yudao-module-mall/yudao-module-product-biz/src/test/resources/sql/create_tables.sql

@@ -1,30 +1,54 @@
-CREATE TABLE IF NOT EXISTS "product_category" (
-    "id" bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY,
-    "parent_id" bigint(20) NOT NULL,
-    "name" varchar(255) NOT NULL,
-    "pic_url" varchar(255) NOT NULL,
-    "sort" int(11) NOT NULL,
-    "description" varchar(1024) NOT NULL,
-    "status" tinyint(4) NOT NULL,
-    "creator" varchar(64) DEFAULT '',
-    "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
-    "updater" varchar(64) DEFAULT '',
-    "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
-    "deleted" bit NOT NULL DEFAULT FALSE,
-    PRIMARY KEY ("id")
-) COMMENT '商品分类';
+CREATE TABLE IF NOT EXISTS `product_sku` (
+`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
+`spu_id` bigint NOT NULL COMMENT 'spu编号',
+`tenant_id` bigint NOT NULL DEFAULT '0' COMMENT '租户编号',
+`name` varchar(128) DEFAULT NULL COMMENT '商品 SKU 名字',
+`properties` varchar(128) DEFAULT NULL COMMENT '规格值数组-json格式, [{propertId: , valueId: }, {propertId: , valueId: }]',
+`price` int NOT NULL DEFAULT '-1' COMMENT '销售价格,单位:分',
+`market_price` int DEFAULT NULL COMMENT '市场价',
+`cost_price` int NOT NULL DEFAULT '-1' COMMENT '成本价,单位: 分',
+`pic_url` varchar(128) NOT NULL COMMENT '图片地址',
+`stock` int DEFAULT NULL COMMENT '库存',
+`warn_stock` int DEFAULT NULL COMMENT '预警库存',
+`volume` double DEFAULT NULL COMMENT '商品体积',
+`weight` double DEFAULT NULL COMMENT '商品重量',
+`bar_code` varchar(64) DEFAULT NULL COMMENT '条形码',
+`status` tinyint DEFAULT NULL COMMENT '状态: 0-正常 1-禁用',
+`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+`creator` varchar(64) DEFAULT NULL COMMENT '创建人',
+`updater` double DEFAULT NULL COMMENT '更新人',
+`deleted` bit(1) NOT NULL DEFAULT 0 COMMENT '是否删除',
+PRIMARY KEY (`id`)
+) COMMENT '商品sku';
 
-CREATE TABLE IF NOT EXISTS "product_brand" (
-    "id" bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY,
-    "name" varchar(255) NOT NULL,
-    "pic_url" varchar(255) NOT NULL,
-    "sort" int(11),
-    "description" varchar(1024),
-    "status" tinyint(4) NOT NULL,
-    "creator" varchar(64) DEFAULT '',
-    "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
-    "updater" varchar(64) DEFAULT '',
-    "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
-    "deleted" bit NOT NULL DEFAULT FALSE,
-    PRIMARY KEY ("id")
-) COMMENT '商品品牌';
+
+CREATE TABLE IF NOT EXISTS `product_spu` (
+`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
+`tenant_id` bigint NOT NULL DEFAULT '0' COMMENT '租户编号',
+`brand_id` bigint DEFAULT NULL COMMENT '商品品牌编号',
+`category_id` bigint NOT NULL COMMENT '分类id',
+`spec_type` int NOT NULL COMMENT '规格类型:0 单规格 1 多规格',
+`code` varchar(128) DEFAULT NULL COMMENT '商品编码',
+`name` varchar(128) NOT NULL COMMENT '商品名称',
+`sell_point` varchar(128) DEFAULT NULL COMMENT '卖点',
+`description` text COMMENT '描述',
+`pic_urls` varchar(1024) DEFAULT '' COMMENT '商品轮播图地址数组,以逗号分隔最多上传15张',
+`video_url` varchar(128) DEFAULT NULL COMMENT '商品视频',
+`market_price` int DEFAULT NULL COMMENT '市场价,单位使用:分',
+`min_price` int DEFAULT NULL COMMENT '最小价格,单位使用:分',
+`max_price` int DEFAULT NULL COMMENT '最大价格,单位使用:分',
+`total_stock` int NOT NULL DEFAULT '0' COMMENT '总库存',
+`show_stock` int DEFAULT '0' COMMENT '是否展示库存',
+`sales_count` int DEFAULT '0' COMMENT '商品销量',
+`virtual_sales_count` int DEFAULT '0' COMMENT '虚拟销量',
+`click_count` int DEFAULT '0' COMMENT '商品点击量',
+`status` bit(1) DEFAULT NULL COMMENT '上下架状态: 0 上架(开启) 1 下架(禁用)-1 回收',
+`sort` int NOT NULL DEFAULT '0' COMMENT '排序字段',
+`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+`creator` varchar(64) DEFAULT NULL COMMENT '创建人',
+`updater` varchar(64) DEFAULT NULL COMMENT '更新人',
+`deleted` bit(1) NOT NULL DEFAULT 0 COMMENT '是否删除',
+PRIMARY KEY (`id`)
+) COMMENT '商品spu';