Browse Source

fix:完善商品管理, 完善 mall.sql, 完善 product 测试相关sql, 完善 ProductSpuServiceImplTest 测试

puhui999 1 năm trước cách đây
mục cha
commit
cac5545762
42 tập tin đã thay đổi với 1317 bổ sung652 xóa
  1. 182 201
      sql/mysql/optional/mall.sql
  2. 10 0
      yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/DictTypeConstants.java
  3. 3 2
      yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/ErrorCodeConstants.java
  4. 34 0
      yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/ProductConstants.java
  5. 10 3
      yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/spu/ProductSpuPageTabEnum.java
  6. 9 1
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/ProductBrandController.java
  7. 17 0
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/ProductBrandSimpleRespVO.java
  8. 45 10
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuBaseVO.java
  9. 0 16
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuCreateOrUpdateReqVO.java
  10. 0 21
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuRespVO.java
  11. 32 10
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.java
  12. 11 7
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuBaseVO.java
  13. 8 3
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuCreateReqVO.java
  14. 19 3
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuDetailRespVO.java
  15. 119 0
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuExcelVO.java
  16. 32 0
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuExportReqVO.java
  17. 12 1
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuPageReqVO.java
  18. 0 32
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuPageRespVO.java
  19. 26 2
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuRespVO.java
  20. 30 8
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuSimpleRespVO.java
  21. 22 3
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuUpdateReqVO.java
  22. 8 1
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuUpdateStatusReqVO.java
  23. 2 1
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/brand/ProductBrandConvert.java
  24. 75 27
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/spu/ProductSpuConvert.java
  25. 0 6
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/category/ProductCategoryDO.java
  26. 2 1
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/property/ProductPropertyDO.java
  27. 2 1
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/property/ProductPropertyValueDO.java
  28. 16 20
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/sku/ProductSkuDO.java
  29. 1 1
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/spu/ProductSpuDO.java
  30. 3 0
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/brand/ProductBrandMapper.java
  31. 85 21
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/spu/ProductSpuMapper.java
  32. 7 0
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/brand/ProductBrandService.java
  33. 5 0
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/brand/ProductBrandServiceImpl.java
  34. 7 5
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/category/ProductCategoryServiceImpl.java
  35. 0 1
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceImpl.java
  36. 8 0
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuService.java
  37. 54 51
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java
  38. 2 1
      yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/category/ProductCategoryServiceImplTest.java
  39. 14 8
      yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceTest.java
  40. 288 113
      yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImplTest.java
  41. 3 1
      yudao-module-mall/yudao-module-product-biz/src/test/resources/sql/clean.sql
  42. 114 70
      yudao-module-mall/yudao-module-product-biz/src/test/resources/sql/create_tables.sql

+ 182 - 201
sql/mysql/optional/mall.sql

@@ -22,23 +22,23 @@ SET FOREIGN_KEY_CHECKS = 0;
 -- ----------------------------
 DROP TABLE IF EXISTS `market_activity`;
 CREATE TABLE `market_activity`  (
-                                    `id` bigint NOT NULL AUTO_INCREMENT COMMENT '活动编号',
-                                    `title` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '活动标题',
-                                    `activity_type` tinyint NOT NULL COMMENT '活动类型',
-                                    `status` tinyint NOT NULL DEFAULT -1 COMMENT '活动状态',
-                                    `start_time` datetime NOT NULL COMMENT '开始时间',
-                                    `end_time` datetime NOT NULL COMMENT '结束时间',
-                                    `invalid_time` datetime NULL DEFAULT NULL COMMENT '失效时间',
-                                    `delete_time` datetime NULL DEFAULT NULL COMMENT '删除时间',
-                                    `time_limited_discount` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '限制折扣字符串,使用 JSON 序列化成字符串存储',
-                                    `full_privilege` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '限制折扣字符串,使用 JSON 序列化成字符串存储',
-                                    `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '创建者',
-                                    `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-                                    `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '更新者',
-                                    `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-                                    `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
-                                    `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
-                                    PRIMARY KEY (`id`) USING BTREE
+    `id` bigint NOT NULL AUTO_INCREMENT COMMENT '活动编号',
+    `title` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '活动标题',
+    `activity_type` tinyint NOT NULL COMMENT '活动类型',
+    `status` tinyint NOT NULL DEFAULT -1 COMMENT '活动状态',
+    `start_time` datetime NOT NULL COMMENT '开始时间',
+    `end_time` datetime NOT NULL COMMENT '结束时间',
+    `invalid_time` datetime NULL DEFAULT NULL COMMENT '失效时间',
+    `delete_time` datetime NULL DEFAULT NULL COMMENT '删除时间',
+    `time_limited_discount` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '限制折扣字符串,使用 JSON 序列化成字符串存储',
+    `full_privilege` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '限制折扣字符串,使用 JSON 序列化成字符串存储',
+    `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '创建者',
+    `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '更新者',
+    `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+    `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
+    `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
+    PRIMARY KEY (`id`) USING BTREE
 ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '促销活动';
 
 -- ----------------------------
@@ -52,20 +52,20 @@ COMMIT;
 -- ----------------------------
 DROP TABLE IF EXISTS `market_banner`;
 CREATE TABLE `market_banner`  (
-                                  `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'Banner编号',
-                                  `title` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT 'Banner标题',
-                                  `pic_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '图片URL',
-                                  `status` tinyint NOT NULL DEFAULT -1 COMMENT '活动状态',
-                                  `url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '跳转地址',
-                                  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '创建者',
-                                  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-                                  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '更新者',
-                                  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-                                  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
-                                  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
-                                  `sort` tinyint NULL DEFAULT NULL COMMENT '排序',
-                                  `memo` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '描述',
-                                  PRIMARY KEY (`id`) USING BTREE
+    `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'Banner编号',
+    `title` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT 'Banner标题',
+    `pic_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '图片URL',
+    `status` tinyint NOT NULL DEFAULT -1 COMMENT '活动状态',
+    `url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '跳转地址',
+    `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '创建者',
+    `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '更新者',
+    `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+    `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
+    `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
+    `sort` tinyint NULL DEFAULT NULL COMMENT '排序',
+    `memo` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '描述',
+    PRIMARY KEY (`id`) USING BTREE
 ) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'Banner管理';
 
 -- ----------------------------
@@ -79,22 +79,22 @@ COMMIT;
 -- ----------------------------
 DROP TABLE IF EXISTS `member_address`;
 CREATE TABLE `member_address`  (
-                                   `id` bigint NOT NULL AUTO_INCREMENT COMMENT '收件地址编号',
-                                   `user_id` bigint NOT NULL COMMENT '用户编号',
-                                   `name` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '收件人名称',
-                                   `mobile` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '手机号',
-                                   `area_id` bigint NOT NULL COMMENT '地区编码',
-                                   `post_code` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '邮编',
-                                   `detail_address` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '收件详细地址',
-                                   `defaulted` bit(1) NOT NULL COMMENT '是否默认',
-                                   `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
-                                   `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-                                   `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
-                                   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-                                   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
-                                   `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
-                                   PRIMARY KEY (`id`) USING BTREE,
-                                   INDEX `idx_userId`(`user_id` ASC) USING BTREE
+    `id` bigint NOT NULL AUTO_INCREMENT COMMENT '收件地址编号',
+    `user_id` bigint NOT NULL COMMENT '用户编号',
+    `name` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '收件人名称',
+    `mobile` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '手机号',
+    `area_id` bigint NOT NULL COMMENT '地区编码',
+    `post_code` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '邮编',
+    `detail_address` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '收件详细地址',
+    `defaulted` bit(1) NOT NULL COMMENT '是否默认',
+    `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
+    `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
+    `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+    `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
+    `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
+    PRIMARY KEY (`id`) USING BTREE,
+    INDEX `idx_userId`(`user_id` ASC) USING BTREE
 ) ENGINE = InnoDB AUTO_INCREMENT = 21 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '用户收件地址';
 
 -- ----------------------------
@@ -109,19 +109,19 @@ COMMIT;
 -- ----------------------------
 DROP TABLE IF EXISTS `product_brand`;
 CREATE TABLE `product_brand`  (
-                                  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '品牌编号',
-                                  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '品牌名称',
-                                  `pic_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '品牌图片',
-                                  `sort` int NULL DEFAULT 0 COMMENT '品牌排序',
-                                  `description` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '品牌描述',
-                                  `status` tinyint NOT NULL COMMENT '状态',
-                                  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
-                                  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-                                  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
-                                  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-                                  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
-                                  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
-                                  PRIMARY KEY (`id`) USING BTREE
+    `id` bigint NOT NULL AUTO_INCREMENT COMMENT '品牌编号',
+    `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '品牌名称',
+    `pic_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '品牌图片',
+    `sort` int NULL DEFAULT 0 COMMENT '品牌排序',
+    `description` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '品牌描述',
+    `status` tinyint NOT NULL COMMENT '状态',
+    `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
+    `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
+    `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+    `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
+    `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
+    PRIMARY KEY (`id`) USING BTREE
 ) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '商品品牌';
 
 -- ----------------------------
@@ -136,20 +136,20 @@ COMMIT;
 -- ----------------------------
 DROP TABLE IF EXISTS `product_category`;
 CREATE TABLE `product_category`  (
-                                     `id` bigint NOT NULL AUTO_INCREMENT COMMENT '分类编号',
-                                     `parent_id` bigint NOT NULL COMMENT '父分类编号',
-                                     `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '分类名称',
-                                     `pic_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '分类图片',
-                                     `sort` int NULL DEFAULT 0 COMMENT '分类排序',
-                                     `description` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '分类描述',
-                                     `status` tinyint NOT NULL COMMENT '开启状态',
-                                     `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
-                                     `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-                                     `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
-                                     `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-                                     `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
-                                     `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
-                                     PRIMARY KEY (`id`) USING BTREE
+    `id` bigint NOT NULL AUTO_INCREMENT COMMENT '分类编号',
+    `parent_id` bigint NOT NULL COMMENT '父分类编号',
+    `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '分类名称',
+    `pic_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '分类图片',
+    `sort` int NULL DEFAULT 0 COMMENT '分类排序',
+    `description` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '分类描述',
+    `status` tinyint NOT NULL COMMENT '开启状态',
+    `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
+    `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
+    `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+    `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
+    `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
+    PRIMARY KEY (`id`) USING BTREE
 ) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '商品分类';
 
 -- ----------------------------
@@ -164,18 +164,18 @@ COMMIT;
 -- ----------------------------
 DROP TABLE IF EXISTS `product_property`;
 CREATE TABLE `product_property` (
-                                    `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
-                                    `name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci 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) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人',
-                                    `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '更新人',
-                                    `tenant_id` bigint NOT NULL DEFAULT '0' COMMENT '租户编号',
-                                    `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
-                                    `remark` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注',
-                                    PRIMARY KEY (`id`) USING BTREE,
-                                    KEY `idx_name` (`name`(32)) USING BTREE COMMENT '规格名称索引'
+    `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci 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) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人',
+    `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '更新人',
+    `tenant_id` bigint NOT NULL DEFAULT '0' COMMENT '租户编号',
+    `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
+    `remark` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注',
+    PRIMARY KEY (`id`) USING BTREE,
+    KEY `idx_name` (`name`(32)) USING BTREE COMMENT '规格名称索引'
 ) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='规格名称';
 
 -- ----------------------------
@@ -189,18 +189,18 @@ COMMIT;
 -- ----------------------------
 DROP TABLE IF EXISTS `product_property_value`;
 CREATE TABLE `product_property_value` (
-                                          `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
-                                          `property_id` bigint DEFAULT NULL COMMENT '规格键id',
-                                          `name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '规格值名字',
-                                          `status` tinyint DEFAULT NULL COMMENT '状态: 1 开启 ,2 禁用',
-                                          `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-                                          `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-                                          `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人',
-                                          `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '更新人',
-                                          `tenant_id` bigint NOT NULL DEFAULT '0' COMMENT '租户编号',
-                                          `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
-                                          `remark` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注',
-                                          PRIMARY KEY (`id`) USING BTREE
+    `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `property_id` bigint DEFAULT NULL COMMENT '规格键id',
+    `name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '规格值名字',
+    `status` tinyint DEFAULT NULL COMMENT '状态: 1 开启 ,2 禁用',
+    `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+    `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人',
+    `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '更新人',
+    `tenant_id` bigint NOT NULL DEFAULT '0' COMMENT '租户编号',
+    `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
+    `remark` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注',
+    PRIMARY KEY (`id`) USING BTREE
 ) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='规格值';
 
 -- ----------------------------
@@ -214,26 +214,26 @@ COMMIT;
 -- ----------------------------
 DROP TABLE IF EXISTS `product_sku`;
 CREATE TABLE `product_sku` (
-                               `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
-                               `spu_id` bigint NOT NULL COMMENT 'spu编号',
-                               `properties` varchar(512) 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 '成本价,单位: 分',
-                               `bar_code` varchar(64)  DEFAULT NULL COMMENT 'SKU 的条形码',
-                               `pic_url` varchar(256)  NOT NULL COMMENT '图片地址',
-                               `stock` int DEFAULT NULL COMMENT '库存',
-                               `weight` double DEFAULT NULL COMMENT '商品重量,单位:kg 千克',
-                               `volume` double DEFAULT NULL COMMENT '商品体积,单位:m^3 平米',
-                               `sub_commission_first_price` int DEFAULT NULL COMMENT '一级分销的佣金,单位:分',
-                               `sub_commission_second_price` int DEFAULT NULL COMMENT '二级分销的佣金,单位:分',
-                               `sales_count` int DEFAULT NULL 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` double(64,0) DEFAULT NULL COMMENT '更新人',
-                               `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
-                               `tenant_id` bigint NOT NULL DEFAULT '0' COMMENT '租户编号',
+    `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `spu_id` bigint NOT NULL COMMENT 'spu编号',
+    `properties` varchar(512) 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 '成本价,单位: 分',
+    `bar_code` varchar(64)  DEFAULT NULL COMMENT 'SKU 的条形码',
+    `pic_url` varchar(256)  NOT NULL COMMENT '图片地址',
+    `stock` int DEFAULT NULL COMMENT '库存',
+    `weight` double DEFAULT NULL COMMENT '商品重量,单位:kg 千克',
+    `volume` double DEFAULT NULL COMMENT '商品体积,单位:m^3 平米',
+    `sub_commission_first_price` int DEFAULT NULL COMMENT '一级分销的佣金,单位:分',
+    `sub_commission_second_price` int DEFAULT NULL COMMENT '二级分销的佣金,单位:分',
+    `sales_count` int DEFAULT NULL 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` double(64,0) DEFAULT NULL COMMENT '更新人',
+    `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
+    `tenant_id` bigint NOT NULL DEFAULT '0' COMMENT '租户编号',
 PRIMARY KEY (`id`) USING BTREE
 ) ENGINE=InnoDB COMMENT='商品sku';
 
@@ -248,45 +248,45 @@ COMMIT;
 -- ----------------------------
 DROP TABLE IF EXISTS `product_spu`;
 CREATE TABLE `product_spu` (
-                               `id` bigint NOT NULL AUTO_INCREMENT COMMENT '商品 SPU 编号,自增',
-                               `name` varchar(128)  NOT NULL COMMENT '商品名称', 
-                               `keyword` varchar(256)  DEFAULT NULL COMMENT '关键字',
-                               `introduction` varchar(256)  COMMENT '商品简介',
-                               `description` text  COMMENT '商品详情', 
-                               `bar_code` varchar(64) DEFAULT NULL COMMENT '条形码',
-                               `category_id` bigint NOT NULL COMMENT '商品分类编号', 
-                               `brand_id` int DEFAULT NULL COMMENT '商品品牌编号',
-                               `pic_url` varchar(256)  NOT NULL COMMENT '商品封面图',
-                               `slider_pic_urls` varchar(2000)  DEFAULT '' COMMENT '商品轮播图地址\n 数组,以逗号分隔\n 最多上传15张',
-                               `video_url` varchar(256)  DEFAULT NULL COMMENT '商品视频', 
-                               `unit` tinyint NOT NULL COMMENT  '单位',
-                               `sort` int NOT NULL DEFAULT '0' COMMENT '排序字段',
-                               `status` tinyint NOT NULL COMMENT '商品状态: 0 上架(开启) 1 下架(禁用)-1 回收',
-                               `spec_type` bit(1) DEFAULT NULL COMMENT '规格类型:0 单规格 1 多规格',
-                               `price` int NOT NULL DEFAULT '-1' COMMENT '商品价格,单位使用:分',
-                               `market_price` int DEFAULT NULL COMMENT '市场价,单位使用:分',
-                               `cost_price` int NOT NULL DEFAULT '-1' COMMENT '成本价,单位: 分',
-                               `stock` int NOT NULL DEFAULT '0' COMMENT '库存',
-                               `delivery_template_id` bigint NOT NULL COMMENT '物流配置模板编号',
-                               `recommend_hot` bit(1) DEFAULT NULL COMMENT '是否热卖推荐: 0 默认 1 热卖',
-                               `recommend_benefit` bit(1) DEFAULT NULL COMMENT '是否优惠推荐: 0 默认 1 优选',
-                               `recommend_best` bit(1) DEFAULT NULL COMMENT '是否精品推荐: 0 默认 1 精品',
-                               `recommend_new` bit(1) DEFAULT NULL COMMENT '是否新品推荐: 0 默认 1 新品',
-                               `recommend_good` bit(1) DEFAULT NULL COMMENT '是否优品推荐',
-                               `give_integral` int NOT NULL COMMENT '赠送积分',
-                               `give_coupon_template_ids` varchar(512) DEFAULT '' COMMENT '赠送的优惠劵编号的数组',
-                               `sub_commission_type` bit(1) DEFAULT NULL COMMENT '分销类型',
-                               `activity_orders` varchar(16) NOT NULL DEFAULT '' COMMENT '活动显示排序0=默认, 1=秒杀,2=砍价,3=拼团',
-                               `sales_count` int DEFAULT '0' COMMENT '商品销量',
-                               `virtual_sales_count` int DEFAULT '0' COMMENT '虚拟销量',
-                               `browse_count` int 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 b'0' COMMENT '是否删除', 
-                               `tenant_id` bigint NOT NULL DEFAULT '0' COMMENT '租户编号',
-                               PRIMARY KEY (`id`) USING BTREE
+    `id` bigint NOT NULL AUTO_INCREMENT COMMENT '商品 SPU 编号,自增',
+    `name` varchar(128)  NOT NULL COMMENT '商品名称',
+    `keyword` varchar(256)  DEFAULT NULL COMMENT '关键字',
+    `introduction` varchar(256)  COMMENT '商品简介',
+    `description` text  COMMENT '商品详情',
+    `bar_code` varchar(64) DEFAULT NULL COMMENT '条形码',
+    `category_id` bigint NOT NULL COMMENT '商品分类编号',
+    `brand_id` int DEFAULT NULL COMMENT '商品品牌编号',
+    `pic_url` varchar(256)  NOT NULL COMMENT '商品封面图',
+    `slider_pic_urls` varchar(2000)  DEFAULT '' COMMENT '商品轮播图地址\n 数组,以逗号分隔\n 最多上传15张',
+    `video_url` varchar(256)  DEFAULT NULL COMMENT '商品视频',
+    `unit` tinyint NOT NULL COMMENT  '单位',
+    `sort` int NOT NULL DEFAULT '0' COMMENT '排序字段',
+    `status` tinyint NOT NULL COMMENT '商品状态: 0 上架(开启) 1 下架(禁用)-1 回收',
+    `spec_type` bit(1) DEFAULT NULL COMMENT '规格类型:0 单规格 1 多规格',
+    `price` int NOT NULL DEFAULT '-1' COMMENT '商品价格,单位使用:分',
+    `market_price` int DEFAULT NULL COMMENT '市场价,单位使用:分',
+    `cost_price` int NOT NULL DEFAULT '-1' COMMENT '成本价,单位: 分',
+    `stock` int NOT NULL DEFAULT '0' COMMENT '库存',
+    `delivery_template_id` bigint NOT NULL COMMENT '物流配置模板编号',
+    `recommend_hot` bit(1) DEFAULT NULL COMMENT '是否热卖推荐: 0 默认 1 热卖',
+    `recommend_benefit` bit(1) DEFAULT NULL COMMENT '是否优惠推荐: 0 默认 1 优选',
+    `recommend_best` bit(1) DEFAULT NULL COMMENT '是否精品推荐: 0 默认 1 精品',
+    `recommend_new` bit(1) DEFAULT NULL COMMENT '是否新品推荐: 0 默认 1 新品',
+    `recommend_good` bit(1) DEFAULT NULL COMMENT '是否优品推荐',
+    `give_integral` int NOT NULL COMMENT '赠送积分',
+    `give_coupon_template_ids` varchar(512) DEFAULT '' COMMENT '赠送的优惠劵编号的数组',
+    `sub_commission_type` bit(1) DEFAULT NULL COMMENT '分销类型',
+    `activity_orders` varchar(16) NOT NULL DEFAULT '' COMMENT '活动显示排序0=默认, 1=秒杀,2=砍价,3=拼团',
+    `sales_count` int DEFAULT '0' COMMENT '商品销量',
+    `virtual_sales_count` int DEFAULT '0' COMMENT '虚拟销量',
+    `browse_count` int 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 b'0' COMMENT '是否删除',
+    `tenant_id` bigint NOT NULL DEFAULT '0' COMMENT '租户编号',
+    PRIMARY KEY (`id`) USING BTREE
 ) ENGINE=InnoDB COMMENT='商品spu';
 
 -- ----------------------------
@@ -294,17 +294,17 @@ CREATE TABLE `product_spu` (
 -- ----------------------------
 DROP TABLE IF EXISTS `product_favorite`;
 CREATE TABLE `product_favorite` (
-   `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号,自增',
-   `spu_id` bigint NOT NULL COMMENT '商品 SPU 编号',
-   `user_id` bigint NOT NULL COMMENT '用户id',
-   `type` int(10) NOT NULL DEFAULT 1 COMMENT '类型1:收藏 2:点赞',
-   `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
-   `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-   `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
-   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
-   `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
-  PRIMARY KEY (`id`) USING BTREE
+    `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号,自增',
+    `spu_id` bigint NOT NULL COMMENT '商品 SPU 编号',
+    `user_id` bigint NOT NULL COMMENT '用户id',
+    `type` int(10) NOT NULL DEFAULT 1 COMMENT '类型1:收藏 2:点赞',
+    `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
+    `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
+    `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+    `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
+    `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
+    PRIMARY KEY (`id`) USING BTREE
 ) ENGINE=InnoDB COMMENT='喜欢的商品表';
 
 
@@ -316,6 +316,7 @@ COMMIT;
 
 SET FOREIGN_KEY_CHECKS = 1;
 
+BEGIN;
 INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2000, '商品中心', '', 1, 60, 0, '/product', 'merchant', NULL, 0, b'1', b'1', '', '2022-07-29 15:53:53', '1', '2022-07-30 22:26:19', b'0');
 INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2002, '商品分类', '', 2, 2, 2000, 'category', 'dict', 'mall/product/category/index', 0, b'1', b'1', '', '2022-07-29 15:53:53', '1', '2022-07-30 22:23:37', b'0');
 INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2003, '分类查询', 'product:category:query', 3, 1, 2002, '', '', '', 0, b'1', b'1', '', '2022-07-29 15:53:53', '', '2022-07-29 15:53:53', b'0');
@@ -334,6 +335,7 @@ INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `
 INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2016, '商品创建', 'product:spu:create', 3, 2, 2014, '', '', '', 0, b'1', b'1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', b'0');
 INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2017, '商品更新', 'product:spu:update', 3, 3, 2014, '', '', '', 0, b'1', b'1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', b'0');
 INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2018, '商品删除', 'product:spu:delete', 3, 4, 2014, '', '', '', 0, b'1', b'1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', b'0');
+INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2018, '商品导出', 'product:spu:export', 3, 5, 2014, '', '', '', 0, b'1', b'1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', b'0');
 INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2019, '规格管理', '', 2, 3, 2000, 'property', '', 'mall/product/property/index', 0, b'1', b'1', '', '2022-08-01 14:55:35', '', '2022-08-01 14:55:35', b'0');
 INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2020, '规格查询', 'product:property:query', 3, 1, 2019, '', '', '', 0, b'1', b'1', '', '2022-08-01 14:55:35', '', '2022-08-01 14:55:35', b'0');
 INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2021, '规格创建', 'product:property:create', 3, 2, 2019, '', '', '', 0, b'1', b'1', '', '2022-08-01 14:55:35', '', '2022-08-01 14:55:35', b'0');
@@ -345,37 +347,16 @@ INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `
 INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2027, 'Banner创建', 'market:banner:create', 3, 2, 2025, '', '', '', 0, b'1', b'1', '', '2022-08-01 14:56:14', '', '2022-08-01 14:56:14', b'0');
 INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2028, 'Banner更新', 'market:banner:update', 3, 3, 2025, '', '', '', 0, b'1', b'1', '', '2022-08-01 14:56:14', '', '2022-08-01 14:56:14', b'0');
 INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2029, 'Banner删除', 'market:banner:delete', 3, 4, 2025, '', '', '', 0, b'1', b'1', '', '2022-08-01 14:56:14', '', '2022-08-01 14:56:14', b'0');
+BEGIN;
 
-INSERT INTO `system_dict_data`(`sort`,`label`,`value`,`dict_type`,`status`,`color_type`,`css_class`,`remark`,`creator`,`create_time`,`updater`,`update_time`,`deleted`) VALUES
-                                                                                                                                                                            (1,'打',2,'product_unit',0,'','','',1, NOW(),1, NOW(),0),
-                                                                                                                                                                            (1,'盒',3,'product_unit',0,'','','',1, NOW(),1, NOW(),0),
-                                                                                                                                                                            (1,'袋',4,'product_unit',0,'','','',1, NOW(),1, NOW(),0),
-                                                                                                                                                                            (1,'箱',5,'product_unit',0,'','','',1, NOW(),1, NOW(),0),
-                                                                                                                                                                            (1,'套',6,'product_unit',0,'','','',1, NOW(),1, NOW(),0),
-                                                                                                                                                                            (1,'包',7,'product_unit',0,'','','',1, NOW(),1, NOW(),0),
-                                                                                                                                                                            (1,'双',8,'product_unit',0,'','','',1, NOW(),1, NOW(),0),
-                                                                                                                                                                            (1,'卷',9,'product_unit',0,'','','',1, NOW(),1, NOW(),0),
-                                                                                                                                                                            (1,'张',10,'product_unit',0,'','','',1, NOW(),1, NOW(),0),
-                                                                                                                                                                            (1,'克',11,'product_unit',0,'','','',1, NOW(),1, NOW(),0),
-                                                                                                                                                                            (1,'千克',12,'product_unit',0,'','','',1, NOW(),1, NOW(),0),
-                                                                                                                                                                            (1,'毫克',13,'product_unit',0,'','','',1, NOW(),1, NOW(),0),
-                                                                                                                                                                            (1,'微克',14,'product_unit',0,'','','',1, NOW(),1, NOW(),0),
-                                                                                                                                                                            (1,'吨',15,'product_unit',0,'','','',1, NOW(),1, NOW(),0),
-                                                                                                                                                                            (1,'升',16,'product_unit',0,'','','',1, NOW(),1, NOW(),0),
-                                                                                                                                                                            (1,'毫升',17,'product_unit',0,'','','',1, NOW(),1, NOW(),0),
-                                                                                                                                                                            (1, '平方米', 18, 'product_unit', 0, '', '', '',  1, NOW(), 1, NOW(), 0),
-                                                                                                                                                                            (1, '平方千米', 19, 'product_unit', 0, '', '', '',  1, NOW(), 1, NOW(), 0),
-                                                                                                                                                                            (1, '平方英里', 20, 'product_unit', 0, '', '', '',  1, NOW(), 1, NOW(), 0),
-                                                                                                                                                                            (1, '平方码', 21, 'product_unit', 0, '', '', '',  1, NOW(), 1, NOW(), 0),
-                                                                                                                                                                            (1, '平方英尺', 22, 'product_unit', 0, '', '', '',  1, NOW(), 1, NOW(), 0),
-                                                                                                                                                                            (1, '立方米', 23, 'product_unit', 0, '', '', '',  1, NOW(), 1, NOW(), 0),
-                                                                                                                                                                            (1, '立方厘米', 24, 'product_unit', 0, '', '', '',  1, NOW(), 1, NOW(), 0),
-                                                                                                                                                                            (1, '立方英寸', 25, 'product_unit', 0, '', '', '',  1, NOW(), 1, NOW(), 0),
-                                                                                                                                                                            (1, '米', 26, 'product_unit', 0, '', '', '',  1, NOW(), 1, NOW(), 0),
-                                                                                                                                                                            (1, '千米', 27, 'product_unit', 0, '', '', '',  1, NOW(), 1, NOW(), 0),
-                                                                                                                                                                            (1, '厘米', 28, 'product_unit', 0, '', '', '', 1, NOW(), 1, NOW(), 0),
-                                                                                                                                                                            (1, '毫米', 29, 'product_unit', 0, '', '', '', 1, NOW(), 1, NOW(), 0),
-                                                                                                                                                                            (1, '英寸', 30, 'product_unit', 0, '', '', '', 1, NOW(), 1, NOW(), 0),
-                                                                                                                                                                            (1, '英尺', 31, 'product_unit', 0, '', '', '', 1, NOW(), 1, NOW(), 0),
-                                                                                                                                                                            (1, '码', 32, 'product_unit', 0, '', '', '', 1, NOW(), 1, NOW(), 0),
-                                                                                                                                                                            (1,'个',1,'product_unit',0,'','','',1, NOW(),1, NOW(),0);
+BEGIN;
+INSERT INTO `system_dict_data`(`sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, '个', 1, 'product_unit', 0, '', '', '', 1, NOW(), 1, NOW(), b'0');
+INSERT INTO `system_dict_data`(`sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, '件', 2, 'product_unit', 0, '', '', '', 1, NOW(), 1, NOW(), b'0');
+INSERT INTO `system_dict_data`(`sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, '盒', 3, 'product_unit', 0, '', '', '', 1, NOW(), 1, NOW(), b'0');
+INSERT INTO `system_dict_data`(`sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, '袋', 4, 'product_unit', 0, '', '', '', 1, NOW(), 1, NOW(), b'0');
+INSERT INTO `system_dict_data`(`sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, '箱', 5, 'product_unit', 0, '', '', '', 1, NOW(), 1, NOW(), b'0');
+INSERT INTO `system_dict_data`(`sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, '套', 6, 'product_unit', 0, '', '', '', 1, NOW(), 1, NOW(), b'0');
+INSERT INTO `system_dict_data`(`sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, '包', 7, 'product_unit', 0, '', '', '', 1, NOW(), 1, NOW(), b'0');
+INSERT INTO `system_dict_data`(`sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, '双', 8, 'product_unit', 0, '', '', '', 1, NOW(), 1, NOW(), b'0');
+INSERT INTO `system_dict_data`(`sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, '卷', 9, 'product_unit', 0, '', '', '', 1, NOW(), 1, NOW(), b'0');
+COMMIT;

+ 10 - 0
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/DictTypeConstants.java

@@ -0,0 +1,10 @@
+package cn.iocoder.yudao.module.product.enums;
+
+/**
+ * product 字典类型的枚举类
+ * @author HUIHUI
+ */
+public interface DictTypeConstants {
+    String PRODUCT_UNIT = "product_unit";  // 商品单位
+    String PRODUCT_SPU_STATUS = "product_spu_status"; // 商品 SPU 状态
+}

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

@@ -18,7 +18,7 @@ public interface ErrorCodeConstants {
 
     // ========== 商品品牌相关编号 1008002000 ==========
     ErrorCode BRAND_NOT_EXISTS = new ErrorCode(1008002000, "品牌不存在");
-    ErrorCode BRAND_DISABLED = new ErrorCode(1008002001, "品牌不存在");
+    ErrorCode BRAND_DISABLED = new ErrorCode(1008002001, "品牌已禁用");
     ErrorCode BRAND_NAME_EXISTS = new ErrorCode(1008002002, "品牌名称已存在");
 
     // ========== 商品属性项 1008003000 ==========
@@ -32,8 +32,9 @@ public interface ErrorCodeConstants {
 
     // ========== 商品 SPU 1008005000 ==========
     ErrorCode SPU_NOT_EXISTS = new ErrorCode(1008005000, "商品 SPU 不存在");
-    ErrorCode SPU_SAVE_FAIL_CATEGORY_LEVEL_ERROR = new ErrorCode(1008005001, "商品分类不正确,原因:必须使用第三级的商品分类下");
+    ErrorCode SPU_SAVE_FAIL_CATEGORY_LEVEL_ERROR = new ErrorCode(1008005001, "商品分类不正确,原因:必须使用第二级的商品分类及以下");
     ErrorCode SPU_NOT_ENABLE = new ErrorCode(1008005002, "商品 SPU 不处于上架状态");
+    ErrorCode SPU_NOT_RECYCLE = new ErrorCode(1008005003, "商品 SPU 不处于回收站状态");
 
     // ========== 商品 SKU 1008006000 ==========
     ErrorCode SKU_NOT_EXISTS = new ErrorCode(1008006000, "商品 SKU 不存在");

+ 34 - 0
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/ProductConstants.java

@@ -0,0 +1,34 @@
+package cn.iocoder.yudao.module.product.enums;
+
+/**
+ * Product 常量 TODO 把使用到的常量收拢到一块定义替换魔法值
+ *
+ * @author HUIHUI
+ */
+
+public interface ProductConstants {
+    /**
+     * 父分类编号 - 根分类
+     */
+    Long PARENT_ID_NULL = 0L;
+    /**
+     * 限定分类层级
+     */
+    int CATEGORY_LEVEL = 2;
+    /**
+     * SPU 分页 tab 个数
+     */
+    int SPU_TAB_COUNTS = 5;
+    /**
+     * 警戒库存 TODO 警戒库存暂时为 10,后期需要使用常量或者数据库配置替换
+     */
+    int ALERT_STOCK = 10;
+    /**
+     * 默认商品销量  TODO 默认商品销量为零
+     */
+    Integer SALES_COUNT = 0;
+    /**
+     * 默认善品浏览量  TODO 默认浏览量为零
+     */
+    Integer BROWSE_COUNT = 0;
+}

+ 10 - 3
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/spu/ProductSpuTabTypeEnum.java → yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/spu/ProductSpuPageTabEnum.java

@@ -1,24 +1,27 @@
 package cn.iocoder.yudao.module.product.enums.spu;
 
+import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
 import lombok.AllArgsConstructor;
 import lombok.Getter;
 
+import java.util.Arrays;
+
 // TODO @puhui999:中英文之间要有空格; 商品 spu Tab 标签枚举;这个类可以改成 ProductSpuPageTabEnum 会更好一点哈;分页 Tab 的意思;
 /**
- * 商品spu标签枚举类型
+ * 商品 spu Tabs 标签枚举类型
  *
  * @author HUIHUI
  */
 @Getter
 @AllArgsConstructor
-public enum ProductSpuTabTypeEnum {
+public enum ProductSpuPageTabEnum implements IntArrayValuable {
 
     FOR_SALE(0,"出售中商品"),
     IN_WAREHOUSE(1,"仓库中商品"),
     SOLD_OUT(2,"已售空商品"),
     ALERT_STOCK(3,"警戒库存"),
     RECYCLE_BIN(4,"商品回收站");
-
+    public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ProductSpuPageTabEnum::getType).toArray();
     /**
      * 状态
      */
@@ -28,4 +31,8 @@ public enum ProductSpuTabTypeEnum {
      */
     private final String name;
 
+    @Override
+    public int[] array() {
+        return ARRAYS;
+    }
 }

+ 9 - 1
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/ProductBrandController.java

@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.module.product.controller.admin.brand;
 
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.product.controller.admin.brand.vo.*;
@@ -61,7 +62,14 @@ public class ProductBrandController {
         ProductBrandDO brand = brandService.getBrand(id);
         return success(ProductBrandConvert.INSTANCE.convert(brand));
     }
-
+    @GetMapping("/list-all-simple")
+    @Operation(summary = "获取品牌精简信息列表", description = "主要用于前端的下拉选项")
+    public CommonResult<List<ProductBrandSimpleRespVO>> getSimpleUserList() {
+        // 获取品牌列表,只要开启状态的
+        List<ProductBrandDO> list = brandService.getBrandListByStatus(CommonStatusEnum.ENABLE.getStatus());
+        // 排序后,返回给前端
+        return success(ProductBrandConvert.INSTANCE.convertList1(list));
+    }
     @GetMapping("/page")
     @Operation(summary = "获得品牌分页")
     @PreAuthorize("@ss.hasPermission('product:brand:query')")

+ 17 - 0
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/ProductBrandSimpleRespVO.java

@@ -0,0 +1,17 @@
+package cn.iocoder.yudao.module.product.controller.admin.brand.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Schema(description = "管理后台 - 品牌精简信息 Response VO")
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class ProductBrandSimpleRespVO {
+    @Schema(description = "品牌编号", required = true, example = "1024")
+    private Long id;
+    @Schema(description = "品牌名称", required = true, example = "芋道")
+    private String name;
+}

+ 45 - 10
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuBaseVO.java

@@ -1,7 +1,11 @@
 package cn.iocoder.yudao.module.product.controller.admin.sku.vo;
 
+import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO;
+import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO;
 import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
 import lombok.Data;
+import lombok.NoArgsConstructor;
 
 import javax.validation.constraints.NotEmpty;
 import javax.validation.constraints.NotNull;
@@ -41,20 +45,51 @@ public class ProductSkuBaseVO {
     @Schema(description = "预警预存", example = "1")
     private Integer warnStock;
 
-    @Schema(description = "商品重量", example = "1") // 单位:kg 千克
+    @Schema(description = "商品重量,单位:kg 千克", example = "1")
     private Double weight;
 
-    @Schema(description = "商品体积", example = "1024") // 单位:m^3 平米
+    @Schema(description = "商品体积,单位:m^3 平米", example = "1024")
     private Double volume;
-    // TODO @pitui999:注释可以去掉,VO 使用 @Schema 作为注释
-    /**
-     * 一级分销的佣金,单位:分
-     */
-    @Schema(description = "一级分销的佣金", example = "1024")
+
+    @Schema(description = "一级分销的佣金,单位:分", example = "1024")
     private Integer subCommissionFirstPrice;
+
+    @Schema(description = "二级分销的佣金,单位:分", example = "1024")
+    private Integer subCommissionSecondPrice;
+
     /**
-     * 二级分销的佣金,单位:分
+     * 商品属性
      */
-    @Schema(description = "二级分销的佣金", example = "1024")
-    private Integer subCommissionSecondPrice;
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class Property {
+
+        /**
+         * 属性编号
+         * 关联 {@link ProductPropertyDO#getId()}
+         */
+        private Long propertyId;
+        /**
+         * 属性名字
+         * 冗余 {@link ProductPropertyDO#getName()}
+         *
+         * 注意:每次属性名字发生变化时,需要更新该冗余
+         */
+        private String propertyName;
+
+        /**
+         * 属性值编号
+         * 关联 {@link ProductPropertyValueDO#getId()}
+         */
+        private Long valueId;
+        /**
+         * 属性值名字
+         * 冗余 {@link ProductPropertyValueDO#getName()}
+         *
+         * 注意:每次属性值名字发生变化时,需要更新该冗余
+         */
+        private String valueName;
+
+    }
 }

+ 0 - 16
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuCreateOrUpdateReqVO.java

@@ -12,22 +12,6 @@ import java.util.List;
 @ToString(callSuper = true)
 public class ProductSkuCreateOrUpdateReqVO extends ProductSkuBaseVO {
 
-    @Schema(description = "商品属性")
-    @Data
-    @AllArgsConstructor
-    @NoArgsConstructor
-    public static class Property {
-
-        @Schema(description = "属性编号", required = true, example = "1")
-        @NotNull(message = "属性编号不能为空")
-        private Long propertyId;
-
-        @Schema(description = "属性值编号", required = true, example = "1024")
-        @NotNull(message = "属性值编号不能为空")
-        private Long valueId;
-
-    }
-
     /**
      * 属性数组
      */

+ 0 - 21
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuRespVO.java

@@ -15,27 +15,6 @@ public class ProductSkuRespVO extends ProductSkuBaseVO {
     @Schema(description = "主键", required = true, example = "1024")
     private Long id;
 
-    @Schema(description = "商品属性")
-    @Data
-    @AllArgsConstructor
-    @NoArgsConstructor
-    public static class Property {
-
-        @Schema(description = "属性编号", required = true, example = "1")
-        @NotNull(message = "属性编号不能为空")
-        private Long propertyId;
-
-        // TODO @puhui999:propertyName 是不是也返回下
-
-        @Schema(description = "属性值编号", required = true, example = "1024")
-        @NotNull(message = "属性值编号不能为空")
-        private Long valueId;
-
-        @Schema(description = "属性值", example = "1024")
-        private String valueName;
-
-    }
-
     /**
      * 属性数组
      */

+ 32 - 10
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.java

@@ -2,6 +2,9 @@ package cn.iocoder.yudao.module.product.controller.admin.spu;
 
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
 import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*;
 import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert;
 import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
@@ -16,12 +19,24 @@ import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
 import javax.validation.Valid;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
+import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
 
+/**
+ * 商品 SPU 相关接口
+ *
+ * @author HUIHUI
+ */
 @Tag(name = "管理后台 - 商品 SPU")
 @RestController
 @RequestMapping("/product/spu")
@@ -30,10 +45,6 @@ public class ProductSpuController {
 
     @Resource
     private ProductSpuService productSpuService;
-    @Resource
-    private ProductSkuService productSkuService;
-    @Resource
-    private ProductPropertyValueService productPropertyValueService;
 
     @PostMapping("/create")
     @Operation(summary = "创建商品 SPU")
@@ -50,7 +61,7 @@ public class ProductSpuController {
         return success(true);
     }
 
-    @PutMapping("/updateStatus")
+    @PutMapping("/update-status")
     @Operation(summary = "更新商品 SPU Status")
     @PreAuthorize("@ss.hasPermission('product:spu:update')")
     public CommonResult<Boolean> updateStatus(@Valid @RequestBody ProductSpuUpdateStatusReqVO updateReqVO) {
@@ -86,16 +97,27 @@ public class ProductSpuController {
     @GetMapping("/page")
     @Operation(summary = "获得商品 SPU 分页")
     @PreAuthorize("@ss.hasPermission('product:spu:query')")
-    public CommonResult<PageResult<ProductSpuPageRespVO>> getSpuPage(@Valid ProductSpuPageReqVO pageVO) {
+    public CommonResult<PageResult<ProductSpuRespVO>> getSpuPage(@Valid ProductSpuPageReqVO pageVO) {
         return success(ProductSpuConvert.INSTANCE.convertPage(productSpuService.getSpuPage(pageVO)));
     }
-    
-    // TODO @tuihui999:get-count;另外,url 使用 - 拆分
-    @GetMapping("/tabsCount")
-    @Operation(summary = "获得商品 SPU tabsCount")
+
+    // TODO @tuihui999:get-count;另外,url 使用 - 拆分  fix
+    @GetMapping("/get-count")
+    @Operation(summary = "获得商品 SPU 分页 tab count")
     @PreAuthorize("@ss.hasPermission('product:spu:query')")
     public CommonResult<Map<Integer, Long>> getTabsCount() {
         return success(productSpuService.getTabsCount());
     }
 
+    @GetMapping("/export")
+    @Operation(summary = "导出用户")
+    @PreAuthorize("@ss.hasPermission('product:spu:export')")
+    @OperateLog(type = EXPORT)
+    public void exportUserList(@Validated ProductSpuExportReqVO reqVO,
+                               HttpServletResponse response) throws IOException {
+        List<ProductSpuDO> spuList = productSpuService.getSpuList(reqVO);
+        // 导出 Excel
+        List<ProductSpuExcelVO> datas = ProductSpuConvert.INSTANCE.convertList03(spuList);
+        ExcelUtils.write(response, "商品spu.xls", "数据", ProductSpuExcelVO.class, datas);
+    }
 }

+ 11 - 7
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuBaseVO.java

@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.module.product.controller.admin.spu.vo;
 
+import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 
@@ -10,7 +11,9 @@ import java.util.List;
 /**
 * 商品 SPU Base VO,提供给添加、修改、详细的子 VO 使用
 * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
-*/
+ *
+ * @author HUIHUI
+ */
 @Data
 public class ProductSpuBaseVO {
 
@@ -30,11 +33,12 @@ public class ProductSpuBaseVO {
     @NotEmpty(message = "商品详情不能为空")
     private String description;
 
-    @Schema(description = "商品分类编号", required = true, example = "芋道")
-    @NotNull(message = "商品分类编号不能为空")
+    @Schema(description = "商品分类编号", required = true, example = "1")
+    @NotNull(message = "商品分类不能为空")
     private Long categoryId;
 
-    @Schema(description = "商品品牌编号", required = true, example = "芋道")
+    @Schema(description = "商品品牌编号", required = true, example = "1")
+    @NotNull(message = "商品品牌不能为空")
     private Long brandId;
 
     @Schema(description = "商品封面图", required = true, example = "芋道")
@@ -96,16 +100,16 @@ public class ProductSpuBaseVO {
     @Schema(description = "赠送的优惠劵编号的数组") // TODO 这块前端还未实现
     private List<Long> giveCouponTemplateIds;
 
-    @Schema(description = "分销类型")
+    @Schema(description = "分销类型", example = "true")
     @NotNull(message = "商品分销类型不能为空")
     private Boolean subCommissionType;
 
-    @Schema(description = "活动展示顺序") // TODO 这块前端还未实现
+    @Schema(description = "活动展示顺序", example = "[1、3、2、4、5]") // TODO 这块前端还未实现
     private List<Integer> activityOrders;
 
     // ========== 统计相关字段 =========
 
-    @Schema(description = "虚拟销量", required = true, example = "芋道")
+    @Schema(description = "虚拟销量", example = "芋道")
     private Integer virtualSalesCount;
 
 }

+ 8 - 3
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuCreateReqVO.java

@@ -9,15 +9,20 @@ import lombok.ToString;
 import javax.validation.Valid;
 import java.util.List;
 
+/**
+ * 商品 SPU 创建 Request VO
+ *
+ * @author HUIHUI
+ */
 @Schema(description = "管理后台 - 商品 SPU 创建 Request VO")
 @Data
 @EqualsAndHashCode(callSuper = true)
 @ToString(callSuper = true)
 public class ProductSpuCreateReqVO extends ProductSpuBaseVO {
 
-    /**
-     * SKU 数组
-     */
+    // ========== SKU 相关字段 =========
+
+    @Schema(description = "SKU 数组")
     @Valid
     private List<ProductSkuCreateOrUpdateReqVO> skus;
 

+ 19 - 3
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuDetailRespVO.java

@@ -6,20 +6,36 @@ import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.ToString;
 
+import java.time.LocalDateTime;
 import java.util.List;
 
-@Schema(description = "管理后台 - 商品 SPU 详细 Response VO") // 包括关联的 SKU 等信息
+/**
+ * 商品 SPU 详细 Response VO
+ * 包括关联的 SKU 等信息
+ *
+ * @author HUIHUI
+ */
+@Schema(description = "管理后台 - 商品 SPU 详细 Response VO")
 @Data
 @EqualsAndHashCode(callSuper = true)
 @ToString(callSuper = true)
 public class ProductSpuDetailRespVO extends ProductSpuBaseVO {
 
-    @Schema(description = "商品编号", example = "1")
+    @Schema(description = "spuId")
     private Long id;
 
+    @Schema(description = "商品销量")
+    private Integer salesCount;
+
+    @Schema(description = "浏览量")
+    private Integer browseCount;
+
+    @Schema(description = "商品状态")
+    private Integer status;
+
     // ========== SKU 相关字段 =========
 
-    @Schema(description = "SKU 数组", example = "1")
+    @Schema(description = "SKU 数组")
     private List<ProductSkuRespVO> skus;
 
 }

+ 119 - 0
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuExcelVO.java

@@ -0,0 +1,119 @@
+package cn.iocoder.yudao.module.product.controller.admin.spu.vo;
+
+import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
+import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
+import cn.iocoder.yudao.module.product.enums.DictTypeConstants;
+import com.alibaba.excel.annotation.ExcelProperty;
+import lombok.Data;
+
+
+import java.time.LocalDateTime;
+
+/**
+ * 商品 Spu Excel 导出 VO TODO 暂定
+ *
+ * @author HUIHUI
+ */
+@Data
+public class ProductSpuExcelVO {
+    @ExcelProperty("商品编号")
+    private Long id;
+
+    @ExcelProperty("商品名称")
+    private String name;
+
+    @ExcelProperty("关键字")
+    private String keyword;
+
+    @ExcelProperty("商品简介")
+    private String introduction;
+
+    @ExcelProperty("商品详情")
+    private String description;
+
+    @ExcelProperty("条形码")
+    private String barCode;
+
+    @ExcelProperty("商品分类编号")
+    private Long categoryId;
+
+    @ExcelProperty("商品品牌编号")
+    private Long brandId;
+
+    @ExcelProperty("商品封面图")
+    private String picUrl;
+
+    @ExcelProperty("商品轮播图地址")
+    private String sliderPicUrls;
+
+    @ExcelProperty("商品视频")
+    private String videoUrl;
+
+    @ExcelProperty(value = "商品单位", converter = DictConvert.class)
+    @DictFormat(DictTypeConstants.PRODUCT_UNIT)
+    private Integer unit;
+
+    @ExcelProperty("排序字段")
+    private Integer sort;
+
+    @ExcelProperty(value = "商品状态", converter = DictConvert.class)
+    @DictFormat(DictTypeConstants.PRODUCT_SPU_STATUS)
+    private Integer status;
+
+    @ExcelProperty("规格类型")
+    private Boolean specType;
+
+    @ExcelProperty("商品价格")
+    private Integer price;
+
+    @ExcelProperty("市场价")
+    private Integer marketPrice;
+
+    @ExcelProperty("成本价")
+    private Integer costPrice;
+
+    @ExcelProperty("库存")
+    private Integer stock;
+
+    @ExcelProperty("物流配置模板编号")
+    private Long deliveryTemplateId;
+
+    @ExcelProperty("是否热卖推荐")
+    private Boolean recommendHot;
+
+    @ExcelProperty("是否优惠推荐")
+    private Boolean recommendBenefit;
+
+    @ExcelProperty("是否精品推荐")
+    private Boolean recommendBest;
+
+    @ExcelProperty("是否新品推荐")
+    private Boolean recommendNew;
+
+    @ExcelProperty("是否优品推荐")
+    private Boolean recommendGood;
+
+    @ExcelProperty("赠送积分")
+    private Integer giveIntegral;
+
+    @ExcelProperty("赠送的优惠劵编号的数组")
+    private String giveCouponTemplateIds;
+
+    @ExcelProperty("分销类型")
+    private Boolean subCommissionType;
+
+    @ExcelProperty("活动显示排序")
+    private String activityOrders;
+
+    @ExcelProperty("商品销量")
+    private Integer salesCount;
+
+    @ExcelProperty("虚拟销量")
+    private Integer virtualSalesCount;
+
+    @ExcelProperty("商品点击量")
+    private Integer browseCount;
+
+    @ExcelProperty("创建时间")
+    private LocalDateTime createTime;
+}

+ 32 - 0
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuExportReqVO.java

@@ -0,0 +1,32 @@
+package cn.iocoder.yudao.module.product.controller.admin.spu.vo;
+
+import cn.iocoder.yudao.framework.common.validation.InEnum;
+import cn.iocoder.yudao.module.product.enums.spu.ProductSpuPageTabEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+@Schema(description = "管理后台 - 商品Spu导出 Request VO,参数和 ProductSpuPageReqVO 是一致的")
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class ProductSpuExportReqVO {
+    @Schema(description = "商品名称", example = "yutou")
+    private String name;
+
+    @Schema(description = "前端请求的tab类型", example = "1")
+    @InEnum(ProductSpuPageTabEnum.class)
+    private Integer tabType;
+
+    @Schema(description = "商品分类编号")
+    private Long categoryId;
+
+    @Schema(description = "创建时间", example = "[2022-07-01 00:00:00,2022-07-01 23:59:59]")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+}

+ 12 - 1
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuPageReqVO.java

@@ -1,16 +1,24 @@
 package cn.iocoder.yudao.module.product.controller.admin.spu.vo;
 
 import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.validation.InEnum;
+import cn.iocoder.yudao.module.product.enums.spu.ProductSpuPageTabEnum;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.ToString;
 import org.springframework.format.annotation.DateTimeFormat;
 
+import javax.validation.constraints.NotNull;
 import java.time.LocalDateTime;
 
 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
 
+/**
+ * 商品 SPU 分页 Request VO
+ *
+ * @author HUIHUI
+ */
 @Schema(description = "管理后台 - 商品 SPU 分页 Request VO")
 @Data
 @EqualsAndHashCode(callSuper = true)
@@ -20,10 +28,13 @@ public class ProductSpuPageReqVO extends PageParam {
     @Schema(description = "商品名称", example = "yutou")
     private String name;
 
-    // TODO @puhui999:加下 @InEnum 校验
     @Schema(description = "前端请求的tab类型", example = "1")
+    @InEnum(ProductSpuPageTabEnum.class)
     private Integer tabType;
 
+    @Schema(description = "商品分类编号")
+    private Long categoryId;
+
     @Schema(description = "创建时间", example = "[2022-07-01 00:00:00,2022-07-01 23:59:59]")
     @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
     private LocalDateTime[] createTime;

+ 0 - 32
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuPageRespVO.java

@@ -1,32 +0,0 @@
-package cn.iocoder.yudao.module.product.controller.admin.spu.vo;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-
-import java.time.LocalDateTime;
-
-// TODO @puhui999:可以直接继承 ProductSpuRespVO 么?一般情况下,多返回一些字段问题不大的;另外,是不是使用 ProductSpuRespVO 替代
-@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;
-
-}

+ 26 - 2
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuRespVO.java

@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.module.product.controller.admin.spu.vo;
 
+import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
@@ -7,16 +8,39 @@ import lombok.ToString;
 
 import java.time.LocalDateTime;
 
+/**
+ * 商品 SPU Response VO
+ * TODO 移除ProductSpuPageRespVO相关应用跟换为ProductSpuRespVO已继承ProductSpuBaseVO 补全表格展示所需属性
+ * @author HUIHUI
+ */
 @Schema(description = "管理后台 - 商品 SPU Response VO")
 @Data
 @EqualsAndHashCode(callSuper = true)
 @ToString(callSuper = true)
 public class ProductSpuRespVO extends ProductSpuBaseVO {
 
-    @Schema(description = "主键", required = true, example = "1")
+    @Schema(description = "spuId")
     private Long id;
 
-    @Schema(description = "创建时间")
+    @Schema(description = "商品价格")
+    private Integer price;
+
+    @Schema(description = "商品销量")
+    private Integer salesCount;
+
+    @Schema(description = "市场价,单位使用:分")
+    private Integer marketPrice;
+
+    @Schema(description = "成本价,单位使用:分")
+    private Integer costPrice;
+
+    @Schema(description = "商品库存")
+    private Integer stock;
+
+    @Schema(description = "商品创建时间")
     private LocalDateTime createTime;
 
+    @Schema(description = "商品状态")
+    private Integer status;
+
 }

+ 30 - 8
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuSimpleRespVO.java

@@ -1,26 +1,48 @@
 package cn.iocoder.yudao.module.product.controller.admin.spu.vo;
 
+import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.ToString;
 
+/**
+ * 商品 SPU 精简 Response VO
+ *  TODO 商品 SPU 精简 VO 暂时没有使用到,用到的时候再按需添加\修改属性
+ * @author HUIHUI
+ */
 @Schema(description = "管理后台 - 商品 SPU 精简 Response VO")
 @Data
-@EqualsAndHashCode(callSuper = true)
 @ToString(callSuper = true)
-public class ProductSpuSimpleRespVO extends ProductSpuBaseVO {
+public class ProductSpuSimpleRespVO {
 
-    @Schema(description = "主键", required = true, example = "1")
+    @Schema(description = "主键")
     private Long id;
 
-    @Schema(description = "商品名称", required = true, example = "芋道")
+    @Schema(description = "商品名称")
     private String name;
 
-    @Schema(description = " 最小价格,单位使用:分", required = true, example = "1024")
-    private Integer minPrice;
+    @Schema(description = "商品价格,单位使用:分")
+    private Integer price;
 
-    @Schema(description = "最大价格,单位使用:分", required = true, example = "1024")
-    private Integer maxPrice;
+    @Schema(description = "商品市场价,单位使用:分")
+    private Integer marketPrice;
+
+    @Schema(description = "商品成本价,单位使用:分")
+    private Integer costPrice;
+
+    @Schema(description = "商品库存")
+    private Integer stock;
+
+    // ========== 统计相关字段 =========
+
+    @Schema(description = "商品销量")
+    private Integer salesCount;
+
+    @Schema(description = "商品虚拟销量")
+    private Integer virtualSalesCount;
+
+    @Schema(description = "商品浏览量")
+    private Integer browseCount;
 
 }

+ 22 - 3
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuUpdateReqVO.java

@@ -1,6 +1,9 @@
 package cn.iocoder.yudao.module.product.controller.admin.spu.vo;
 
+import cn.iocoder.yudao.framework.common.validation.InEnum;
 import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO;
+import cn.iocoder.yudao.module.product.enums.spu.ProductSpuPageTabEnum;
+import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
@@ -8,8 +11,14 @@ import lombok.ToString;
 
 import javax.validation.Valid;
 import javax.validation.constraints.NotNull;
+import java.time.LocalDateTime;
 import java.util.List;
 
+/**
+ * 商品 SPU 更新 Request VO
+ *
+ * @author HUIHUI
+ */
 @Schema(description = "管理后台 - 商品 SPU 更新 Request VO")
 @Data
 @EqualsAndHashCode(callSuper = true)
@@ -20,9 +29,19 @@ public class ProductSpuUpdateReqVO extends ProductSpuBaseVO {
     @NotNull(message = "商品编号不能为空")
     private Long id;
 
-    /**
-     * SKU 数组
-     */
+    @Schema(description = "商品销量")
+    private Integer salesCount;
+
+    @Schema(description = "浏览量")
+    private Integer browseCount;
+
+    @Schema(description = "商品状态")
+    @InEnum(ProductSpuStatusEnum.class)
+    private Integer status;
+
+    // ========== SKU 相关字段 =========
+
+    @Schema(description = "SKU 数组")
     @Valid
     private List<ProductSkuCreateOrUpdateReqVO> skus;
 

+ 8 - 1
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuUpdateStatusReqVO.java

@@ -1,6 +1,8 @@
 package cn.iocoder.yudao.module.product.controller.admin.spu.vo;
 
+import cn.iocoder.yudao.framework.common.validation.InEnum;
 import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO;
+import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
@@ -10,6 +12,11 @@ import javax.validation.Valid;
 import javax.validation.constraints.NotNull;
 import java.util.List;
 
+/**
+ * 商品 SPU Status 更新 Request VO
+ *
+ * @author HUIHUI
+ */
 @Schema(description = "管理后台 - 商品 SPU Status 更新 Request VO")
 @Data
 public class ProductSpuUpdateStatusReqVO{
@@ -20,7 +27,7 @@ public class ProductSpuUpdateStatusReqVO{
 
     @Schema(description = "商品状态", required = true, example = "1")
     @NotNull(message = "商品状态不能为空")
+    @InEnum(ProductSpuStatusEnum.class)
     private Integer status;
 
-
 }

+ 2 - 1
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/brand/ProductBrandConvert.java

@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.product.convert.brand;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.product.controller.admin.brand.vo.ProductBrandCreateReqVO;
 import cn.iocoder.yudao.module.product.controller.admin.brand.vo.ProductBrandRespVO;
+import cn.iocoder.yudao.module.product.controller.admin.brand.vo.ProductBrandSimpleRespVO;
 import cn.iocoder.yudao.module.product.controller.admin.brand.vo.ProductBrandUpdateReqVO;
 import cn.iocoder.yudao.module.product.dal.dataobject.brand.ProductBrandDO;
 import org.mapstruct.Mapper;
@@ -25,7 +26,7 @@ public interface ProductBrandConvert {
     ProductBrandDO convert(ProductBrandUpdateReqVO bean);
 
     ProductBrandRespVO convert(ProductBrandDO bean);
-
+    List<ProductBrandSimpleRespVO> convertList1(List<ProductBrandDO> list);
     List<ProductBrandRespVO> convertList(List<ProductBrandDO> list);
 
     PageResult<ProductBrandRespVO> convertPage(PageResult<ProductBrandDO> page);

+ 75 - 27
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/spu/ProductSpuConvert.java

@@ -1,6 +1,8 @@
 package cn.iocoder.yudao.module.product.convert.spu;
+import java.time.LocalDateTime;
 
 import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
 import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueDetailRespVO;
@@ -10,6 +12,7 @@ import cn.iocoder.yudao.module.product.controller.app.property.vo.value.AppProdu
 import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuDetailRespVO;
 import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO;
 import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageItemRespVO;
+import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert;
 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.service.property.bo.ProductPropertyValueDetailRespBO;
@@ -19,6 +22,9 @@ import org.mapstruct.factory.Mappers;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
 
 import static cn.hutool.core.util.ObjectUtil.defaultIfNull;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
@@ -34,46 +40,63 @@ public interface ProductSpuConvert {
     ProductSpuConvert INSTANCE = Mappers.getMapper(ProductSpuConvert.class);
 
     ProductSpuDO convert(ProductSpuCreateReqVO bean);
-    // TODO 还是使用convert,重命名改动太多
+
     ProductSpuDO convert(ProductSpuUpdateReqVO bean);
 
     List<ProductSpuDO> convertList(List<ProductSpuDO> list);
 
-    PageResult<ProductSpuPageRespVO> convertPage(PageResult<ProductSpuDO> page);
+    PageResult<ProductSpuRespVO> convertPage(PageResult<ProductSpuDO> page);
 
     ProductSpuPageReqVO convert(AppProductSpuPageReqVO bean);
 
     List<ProductSpuRespDTO> convertList2(List<ProductSpuDO> list);
 
     List<ProductSpuSimpleRespVO> convertList02(List<ProductSpuDO> list);
-
-    // TODO @puhui999:是不是可以删除啦
-    default ProductSpuDetailRespVO convert03(ProductSpuDO spu, List<ProductSkuDO> skus,
-                                             List<ProductPropertyValueDetailRespBO> propertyValues) {
-        ProductSpuDetailRespVO spuVO = convert03(spu);
-        spuVO.setSkus(convertList04(skus));
-        // 处理商品属性
-        Map<Long, ProductPropertyValueDetailRespBO> propertyValueMap = convertMap(propertyValues, ProductPropertyValueDetailRespBO::getValueId);
-        for (int i = 0; i < skus.size(); i++) {
-            List<ProductSkuDO.Property> properties = skus.get(i).getProperties();
-            if (CollUtil.isEmpty(properties)) {
-                continue;
-            }
-            ProductSkuRespVO sku = spuVO.getSkus().get(i);
-            sku.setProperties(new ArrayList<>(properties.size()));
-            // 遍历每个 properties,设置到 AppSpuDetailRespVO.Sku 中
-            properties.forEach(property -> {
-                ProductPropertyValueDetailRespBO propertyValue = propertyValueMap.get(property.getValueId());
-                if (propertyValue == null) {
-                    return;
-                }
-                //sku.getProperties().add(convert04(propertyValue)); TODO 需要重写
-            });
-        }
-        return spuVO;
+    default List<ProductSpuExcelVO> convertList03(List<ProductSpuDO> list){
+        ArrayList<ProductSpuExcelVO> spuExcelVOs = new ArrayList<>();
+        list.forEach((spu)->{
+            ProductSpuExcelVO spuExcelVO = new ProductSpuExcelVO();
+            spuExcelVO.setId(spu.getId());
+            spuExcelVO.setName(spu.getName());
+            spuExcelVO.setKeyword(spu.getKeyword());
+            spuExcelVO.setIntroduction(spu.getIntroduction());
+            spuExcelVO.setDescription(spu.getDescription());
+            spuExcelVO.setBarCode(spu.getBarCode());
+            spuExcelVO.setCategoryId(spu.getCategoryId());
+            spuExcelVO.setBrandId(spu.getBrandId());
+            spuExcelVO.setPicUrl(spu.getPicUrl());
+            spuExcelVO.setSliderPicUrls(StrUtil.toString(spu.getSliderPicUrls()));
+            spuExcelVO.setVideoUrl(spu.getVideoUrl());
+            spuExcelVO.setUnit(spu.getUnit());
+            spuExcelVO.setSort(spu.getSort());
+            spuExcelVO.setStatus(spu.getStatus());
+            spuExcelVO.setSpecType(spu.getSpecType());
+            spuExcelVO.setPrice(spu.getPrice()/100);
+            spuExcelVO.setMarketPrice(spu.getMarketPrice()/100);
+            spuExcelVO.setCostPrice(spu.getCostPrice()/100);
+            spuExcelVO.setStock(spu.getStock());
+            spuExcelVO.setDeliveryTemplateId(spu.getDeliveryTemplateId());
+            spuExcelVO.setRecommendHot(spu.getRecommendHot());
+            spuExcelVO.setRecommendBenefit(spu.getRecommendBenefit());
+            spuExcelVO.setRecommendBest(spu.getRecommendBest());
+            spuExcelVO.setRecommendNew(spu.getRecommendNew());
+            spuExcelVO.setRecommendGood(spu.getRecommendGood());
+            spuExcelVO.setGiveIntegral(spu.getGiveIntegral());
+            spuExcelVO.setGiveCouponTemplateIds(StrUtil.toString(spu.getGiveCouponTemplateIds())); // TODO 暂定
+            spuExcelVO.setSubCommissionType(spu.getSubCommissionType());
+            spuExcelVO.setActivityOrders(StrUtil.toString(spu.getActivityOrders())); // TODO 暂定
+            spuExcelVO.setSalesCount(spu.getSalesCount());
+            spuExcelVO.setVirtualSalesCount(spu.getVirtualSalesCount());
+            spuExcelVO.setBrowseCount(spu.getBrowseCount());
+            spuExcelVO.setCreateTime(spu.getCreateTime());
+            spuExcelVOs.add(spuExcelVO);
+        });
+        return spuExcelVOs;
     }
     ProductSpuDetailRespVO convert03(ProductSpuDO spu);
+
     List<ProductSkuRespVO> convertList04(List<ProductSkuDO> skus);
+
     ProductPropertyValueDetailRespVO convert04(ProductPropertyValueDetailRespBO propertyValue);
 
     // ========== 用户 App 相关 ==========
@@ -84,6 +107,7 @@ public interface ProductSpuConvert {
         // 然后进行转换
         return convertPageForGetSpuPage0(page);
     }
+
     PageResult<AppProductSpuPageItemRespVO> convertPageForGetSpuPage0(PageResult<ProductSpuDO> page);
 
     default AppProductSpuDetailRespVO convertForGetSpuDetail(ProductSpuDO spu, List<ProductSkuDO> skus,
@@ -111,8 +135,32 @@ public interface ProductSpuConvert {
         }
         return spuVO;
     }
+
     AppProductSpuDetailRespVO convertForGetSpuDetail(ProductSpuDO spu);
+
     List<AppProductSpuDetailRespVO.Sku> convertListForGetSpuDetail(List<ProductSkuDO> skus);
+
     AppProductPropertyValueDetailRespVO convertForGetSpuDetail(ProductPropertyValueDetailRespBO propertyValue);
 
+    default ProductSpuDetailRespVO convertForSpuDetailRespVO(ProductSpuDO spu, List<ProductSkuDO> skus, Function<Set<Long>, List<ProductPropertyValueDetailRespBO>> func) {
+        ProductSpuDetailRespVO productSpuDetailRespVO = convert03(spu);
+        if (CollUtil.isNotEmpty(skus)) {
+            List<ProductSkuRespVO> skuVOs = ProductSkuConvert.INSTANCE.convertList(skus);
+            // fix:统一模型,即使是单规格,也查询下,如若Properties为空报错则为单属性不做处理
+            try {
+                // 获取所有的属性值 id
+                Set<Long> valueIds = skus.stream().flatMap(p -> p.getProperties().stream())
+                        .map(ProductSkuDO.Property::getValueId)
+                        .collect(Collectors.toSet());
+                List<ProductPropertyValueDetailRespBO> valueDetailList = func.apply(valueIds);
+                Map<Long, String> stringMap = valueDetailList.stream().collect(Collectors.toMap(ProductPropertyValueDetailRespBO::getValueId, ProductPropertyValueDetailRespBO::getValueName));
+                // 设置属性值名称
+                skuVOs.stream().flatMap(p -> p.getProperties().stream()).forEach(item -> item.setValueName(stringMap.get(item.getValueId())));
+            } catch (Exception ignored) {
+            }
+            productSpuDetailRespVO.setSkus(skuVOs);
+        }
+        return productSpuDetailRespVO;
+    }
+
 }

+ 0 - 6
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/category/ProductCategoryDO.java

@@ -19,12 +19,6 @@ import lombok.*;
 @NoArgsConstructor
 @AllArgsConstructor
 public class ProductCategoryDO extends BaseDO {
-
-    /**
-     * 父分类编号 - 根分类
-     */
-    public static final Long PARENT_ID_NULL = 0L;
-
     /**
      * 分类编号
      */

+ 2 - 1
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/property/ProductPropertyDO.java

@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.module.product.dal.dataobject.property;
 
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
 import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
 import com.baomidou.mybatisplus.annotation.KeySequence;
 import com.baomidou.mybatisplus.annotation.TableId;
@@ -19,7 +20,7 @@ import lombok.*;
 @Builder
 @NoArgsConstructor
 @AllArgsConstructor
-public class ProductPropertyDO extends TenantBaseDO { // TODO @puhui999:这里是不是用 BaseDO 就可以了?
+public class ProductPropertyDO extends BaseDO {
 
     /**
      * 主键

+ 2 - 1
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/property/ProductPropertyValueDO.java

@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.module.product.dal.dataobject.property;
 
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
 import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
 import com.baomidou.mybatisplus.annotation.KeySequence;
 import com.baomidou.mybatisplus.annotation.TableId;
@@ -20,7 +21,7 @@ import lombok.*;
 @Builder
 @NoArgsConstructor
 @AllArgsConstructor
-public class ProductPropertyValueDO extends TenantBaseDO {  // TODO @puhui999:这里是不是用 BaseDO 就可以了?
+public class ProductPropertyValueDO extends BaseDO {
 
     /**
      * 主键

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

@@ -1,7 +1,7 @@
 package cn.iocoder.yudao.module.product.dal.dataobject.sku;
 
 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
-import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
 import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO;
 import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO;
 import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
@@ -27,7 +27,7 @@ import java.util.List;
 @Builder
 @NoArgsConstructor
 @AllArgsConstructor
-public class ProductSkuDO extends TenantBaseDO {  // TODO @puhui999:这里是不是用 BaseDO 就可以了?
+public class ProductSkuDO extends BaseDO {
 
     /**
      * 商品 SKU 编号,自增
@@ -105,33 +105,29 @@ public class ProductSkuDO extends TenantBaseDO {  // TODO @puhui999:这里是
 
         /**
          * 属性编号
-         *
          * 关联 {@link ProductPropertyDO#getId()}
          */
         private Long propertyId;
-        ///**
-        // * 属性名字
-        // *
-        // * 冗余 {@link ProductPropertyDO#getName()}
-        // *
-        // * 注意:每次属性名字发生变化时,需要更新该冗余
-        // */ TODO @puhui999:与已有代码逻辑存在冲突;芋艿:冲突点是啥呀?
-        //private String propertyName;
+        /**
+         * 属性名字
+         * 冗余 {@link ProductPropertyDO#getName()}
+         *
+         * 注意:每次属性名字发生变化时,需要更新该冗余
+         */
+        private String propertyName;
 
         /**
          * 属性值编号
-         *
          * 关联 {@link ProductPropertyValueDO#getId()}
          */
         private Long valueId;
-        ///**
-        // * 属性值名字
-        // *
-        // * 冗余 {@link ProductPropertyValueDO#getName()}
-        // *
-        // * 注意:每次属性值名字发生变化时,需要更新该冗余
-        // */ TODO @puhui999:与已有代码逻辑存在冲突;芋艿:冲突点是啥呀?
-        //private String valueName;
+        /**
+         * 属性值名字
+         * 冗余 {@link ProductPropertyValueDO#getName()}
+         *
+         * 注意:每次属性值名字发生变化时,需要更新该冗余
+         */
+        private String valueName;
 
     }
 

+ 1 - 1
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/spu/ProductSpuDO.java

@@ -29,7 +29,7 @@ import java.util.List;
 @Builder
 @NoArgsConstructor
 @AllArgsConstructor
-public class ProductSpuDO extends TenantBaseDO {
+public class ProductSpuDO extends BaseDO {
 
     /**
      * 商品 SPU 编号,自增

+ 3 - 0
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/brand/ProductBrandMapper.java

@@ -31,4 +31,7 @@ public interface ProductBrandMapper extends BaseMapperX<ProductBrandDO> {
         return selectOne(ProductBrandDO::getName, name);
     }
 
+    default List<ProductBrandDO> selectListByStatus(Integer status){
+        return selectList(ProductBrandDO::getStatus,status);
+    }
 }

+ 85 - 21
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/spu/ProductSpuMapper.java

@@ -1,40 +1,56 @@
 package cn.iocoder.yudao.module.product.dal.mysql.spu;
 
 import cn.hutool.core.util.ObjUtil;
+import cn.hutool.core.util.ObjectUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuExportReqVO;
 import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuPageReqVO;
 import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO;
 import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
+import cn.iocoder.yudao.module.product.enums.ProductConstants;
 import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
-import cn.iocoder.yudao.module.product.enums.spu.ProductSpuTabTypeEnum;
+import cn.iocoder.yudao.module.product.enums.spu.ProductSpuPageTabEnum;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.poi.ss.formula.functions.T;
 
+import java.util.List;
 import java.util.Objects;
 import java.util.Set;
 
 @Mapper
 public interface ProductSpuMapper extends BaseMapperX<ProductSpuDO> {
 
+    /**
+     * 获取 商品 SPU 分页列表数据
+     *
+     * @param reqVO 分页请求参数
+     * @return 商品 SPU 分页列表数据
+     */
     default PageResult<ProductSpuDO> selectPage(ProductSpuPageReqVO reqVO) {
-        // TODO @puhui999:多个 tab,写 if else 去补条件,可阅读性会好点哈
-        return selectPage(reqVO, new LambdaQueryWrapperX<ProductSpuDO>()
-                // 商品名称
+        Integer tabType = reqVO.getTabType();
+        LambdaQueryWrapperX<ProductSpuDO> queryWrapper = new LambdaQueryWrapperX<ProductSpuDO>()
                 .likeIfPresent(ProductSpuDO::getName, reqVO.getName())
+                .eqIfPresent(ProductSpuDO::getCategoryId, reqVO.getCategoryId())
                 .betweenIfPresent(ProductSpuDO::getCreateTime, reqVO.getCreateTime())
-                // 出售中商品
-                .eq(ProductSpuTabTypeEnum.FOR_SALE.getType().equals(reqVO.getTabType()),ProductSpuDO::getStatus,ProductSpuStatusEnum.ENABLE.getStatus())
-                // 仓储中商品
-                .eq(ProductSpuTabTypeEnum.IN_WAREHOUSE.getType().equals(reqVO.getTabType()),ProductSpuDO::getStatus,ProductSpuStatusEnum.DISABLE.getStatus())
-                // 已售空商品
-                .eq(ProductSpuTabTypeEnum.SOLD_OUT.getType().equals(reqVO.getTabType()),ProductSpuDO::getStock,0)
-                // TODO @phuui999:警戒库存暂时为 10,后期需要使用常量或者数据库配置替换
-                .le(ProductSpuTabTypeEnum.ALERT_STOCK.getType().equals(reqVO.getTabType()),ProductSpuDO::getStock,10)
-                // 回收站
-                .eq(ProductSpuTabTypeEnum.RECYCLE_BIN.getType().equals(reqVO.getTabType()),ProductSpuDO::getStatus,ProductSpuStatusEnum.RECYCLE.getStatus())
-                .orderByDesc(ProductSpuDO::getSort));
+                .orderByDesc(ProductSpuDO::getSort);
+        validateTabType(tabType, queryWrapper);
+        return selectPage(reqVO, queryWrapper);
+    }
+
+    /**
+     * 获取库存小于value且状态不等于status的的个数
+     */
+    default Long selectCountByStockAndStatus() {
+        LambdaQueryWrapperX<ProductSpuDO> queryWrapper = new LambdaQueryWrapperX<>();
+        queryWrapper.le(ProductSpuDO::getStock, ProductConstants.ALERT_STOCK)
+                // 如果库存触发警戒库存且状态为回收站的话则不计入触发警戒库存的个数
+                .and(q -> q.ne(ProductSpuDO::getStatus, ProductSpuStatusEnum.RECYCLE.getStatus()));
+        return selectCount(queryWrapper);
     }
 
     /**
@@ -42,10 +58,12 @@ public interface ProductSpuMapper extends BaseMapperX<ProductSpuDO> {
      */
     default PageResult<ProductSpuDO> selectPage(AppProductSpuPageReqVO pageReqVO, Set<Long> categoryIds) {
         LambdaQueryWrapperX<ProductSpuDO> query = new LambdaQueryWrapperX<ProductSpuDO>()
-                .likeIfPresent(ProductSpuDO::getName, pageReqVO.getKeyword()) // 关键字匹配,目前只匹配商品名
-                .inIfPresent(ProductSpuDO::getCategoryId, categoryIds); // 分类
-        query.eq(ProductSpuDO::getStatus, ProductSpuStatusEnum.ENABLE.getStatus()) // 上架状态
-                .gt(ProductSpuDO::getStock, 0); // 有库存
+                // 关键字匹配,目前只匹配商品名
+                .likeIfPresent(ProductSpuDO::getName, pageReqVO.getKeyword())
+                // 分类
+                .inIfPresent(ProductSpuDO::getCategoryId, categoryIds);
+        // 上架状态 且有库存
+        query.eq(ProductSpuDO::getStatus, ProductSpuStatusEnum.ENABLE.getStatus()).gt(ProductSpuDO::getStock, 0);
         // 推荐类型的过滤条件
         if (ObjUtil.equal(pageReqVO.getRecommendType(), AppProductSpuPageReqVO.RECOMMEND_TYPE_HOT)) {
             query.eq(ProductSpuDO::getRecommendHot, true);
@@ -66,14 +84,60 @@ public interface ProductSpuMapper extends BaseMapperX<ProductSpuDO> {
     /**
      * 更新商品 SPU 库存
      *
-     * @param id 商品 SPU 编号
+     * @param id        商品 SPU 编号
      * @param incrCount 增加的库存数量
      */
     default void updateStock(Long id, Integer incrCount) {
         LambdaUpdateWrapper<ProductSpuDO> updateWrapper = new LambdaUpdateWrapper<ProductSpuDO>()
-                .setSql(" total_stock = total_stock +" + incrCount) // 负数,所以使用 + 号
+                // 负数,所以使用 + 号
+                .setSql(" stock = stock +" + incrCount)
                 .eq(ProductSpuDO::getId, id);
         update(null, updateWrapper);
     }
 
+    /**
+     * 获得 Spu 列表
+     *
+     * @param reqVO 查询条件
+     * @return Spu 列表
+     */
+    default List<ProductSpuDO> selectList(ProductSpuExportReqVO reqVO){
+        Integer tabType = reqVO.getTabType();
+        LambdaQueryWrapperX<ProductSpuDO> queryWrapper = new LambdaQueryWrapperX<>();
+        queryWrapper.eqIfPresent(ProductSpuDO::getName,reqVO.getName());
+        queryWrapper.eqIfPresent(ProductSpuDO::getCategoryId,reqVO.getCategoryId());
+        queryWrapper.betweenIfPresent(ProductSpuDO::getCreateTime,reqVO.getCreateTime());
+        validateTabType(tabType, queryWrapper);
+        return selectList(queryWrapper);
+    }
+
+    /**
+     * 验证选项卡类型构建条件
+     *
+     * @param tabType      标签类型
+     * @param queryWrapper 查询条件
+     */
+    static void validateTabType(Integer tabType, LambdaQueryWrapperX<ProductSpuDO> queryWrapper) {
+        if (ObjectUtil.equals(ProductSpuPageTabEnum.FOR_SALE.getType(), tabType)) {
+            // 出售中商品
+            queryWrapper.eqIfPresent(ProductSpuDO::getStatus, ProductSpuStatusEnum.ENABLE.getStatus());
+        }
+        if (ObjectUtil.equals(ProductSpuPageTabEnum.IN_WAREHOUSE.getType(), tabType)) {
+            // 仓储中商品
+            queryWrapper.eqIfPresent(ProductSpuDO::getStatus, ProductSpuStatusEnum.DISABLE.getStatus());
+        }
+        if (ObjectUtil.equals(ProductSpuPageTabEnum.SOLD_OUT.getType(), tabType)) {
+            // 已售空商品
+            queryWrapper.eqIfPresent(ProductSpuDO::getStock, 0);
+        }
+        if (ObjectUtil.equals(ProductSpuPageTabEnum.ALERT_STOCK.getType(), tabType)) {
+            queryWrapper.le(ProductSpuDO::getStock, ProductConstants.ALERT_STOCK)
+                    // 如果库存触发警戒库存且状态为回收站的话则不在警戒库存列表展示
+                    .and(q -> q.ne(ProductSpuDO::getStatus, ProductSpuStatusEnum.RECYCLE.getStatus()));
+        }
+        if (ObjectUtil.equals(ProductSpuPageTabEnum.RECYCLE_BIN.getType(), tabType)) {
+            // 回收站
+            queryWrapper.eqIfPresent(ProductSpuDO::getStatus, ProductSpuStatusEnum.RECYCLE.getStatus());
+        }
+    }
 }

+ 7 - 0
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/brand/ProductBrandService.java

@@ -76,4 +76,11 @@ public interface ProductBrandService {
      */
     PageResult<ProductBrandDO> getBrandPage(ProductBrandPageReqVO pageReqVO);
 
+    /**
+     * 获取指定状态的品牌列表
+     *
+     * @param status 状态
+     * @return  返回品牌列表
+     */
+    List<ProductBrandDO> getBrandListByStatus(Integer status);
 }

+ 5 - 0
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/brand/ProductBrandServiceImpl.java

@@ -114,4 +114,9 @@ public class ProductBrandServiceImpl implements ProductBrandService {
         return brandMapper.selectPage(pageReqVO);
     }
 
+    @Override
+    public List<ProductBrandDO> getBrandListByStatus(Integer status) {
+        return brandMapper.selectListByStatus(status);
+    }
+
 }

+ 7 - 5
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/category/ProductCategoryServiceImpl.java

@@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCateg
 import cn.iocoder.yudao.module.product.convert.category.ProductCategoryConvert;
 import cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO;
 import cn.iocoder.yudao.module.product.dal.mysql.category.ProductCategoryMapper;
+import cn.iocoder.yudao.module.product.enums.ProductConstants;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
@@ -68,7 +69,7 @@ public class ProductCategoryServiceImpl implements ProductCategoryService {
 
     private void validateParentProductCategory(Long id) {
         // 如果是根分类,无需验证
-        if (Objects.equals(id, ProductCategoryDO.PARENT_ID_NULL)) {
+        if (Objects.equals(id, ProductConstants.PARENT_ID_NULL)) {
             return;
         }
         // 父分类不存在
@@ -77,7 +78,7 @@ public class ProductCategoryServiceImpl implements ProductCategoryService {
             throw exception(CATEGORY_PARENT_NOT_EXISTS);
         }
         // 父分类不能是二级分类
-        if (!Objects.equals(category.getParentId(), ProductCategoryDO.PARENT_ID_NULL)) {
+        if (!Objects.equals(category.getParentId(), ProductConstants.PARENT_ID_NULL)) {
             throw exception(CATEGORY_PARENT_NOT_FIRST_LEVEL);
         }
     }
@@ -107,15 +108,16 @@ public class ProductCategoryServiceImpl implements ProductCategoryService {
 
     @Override
     public Integer getCategoryLevel(Long id) {
-        if (Objects.equals(id, ProductCategoryDO.PARENT_ID_NULL)) {
+        if (Objects.equals(id, ProductConstants.PARENT_ID_NULL)) {
             return 0;
         }
         int level = 1;
-        for (int i = 0; i < 100; i++) {
+        // fix: 循环次数不确定改为while循环
+        while (true){
             ProductCategoryDO category = productCategoryMapper.selectById(id);
             // 如果没有父节点,break 结束
             if (category == null
-                    || Objects.equals(category.getParentId(), ProductCategoryDO.PARENT_ID_NULL)) {
+                    || Objects.equals(category.getParentId(), ProductConstants.PARENT_ID_NULL)) {
                 break;
             }
             // 继续递归父节点

+ 0 - 1
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceImpl.java

@@ -176,7 +176,6 @@ public class ProductSkuServiceImpl implements ProductSkuService {
             Long existsSkuId = existsSkuMap.remove(propertiesKey);
             if (existsSkuId != null) {
                 sku.setId(existsSkuId);
-                // TODO 那spuId岂不是为null了
                 updateSkus.add(sku);
                 return;
             }

+ 8 - 0
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuService.java

@@ -74,6 +74,14 @@ public interface ProductSpuService {
      */
     List<ProductSpuDO> getSpuList();
 
+    /**
+     * 获得所有商品 SPU 列表
+     *
+     * @param reqVO 导出条件
+     * @return 商品 SPU 列表
+     */
+    List<ProductSpuDO> getSpuList(ProductSpuExportReqVO reqVO);
+
     /**
      * 获得商品 SPU 分页,提供给挂你兰后台使用
      *

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

@@ -10,11 +10,13 @@ import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*;
 import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO;
 import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert;
 import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert;
+import cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO;
 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 cn.iocoder.yudao.module.product.enums.ProductConstants;
 import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
-import cn.iocoder.yudao.module.product.enums.spu.ProductSpuTabTypeEnum;
+import cn.iocoder.yudao.module.product.enums.spu.ProductSpuPageTabEnum;
 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.property.ProductPropertyValueService;
@@ -31,8 +33,7 @@ import java.util.stream.Collectors;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getSumValue;
-import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_EXISTS;
-import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_SAVE_FAIL_CATEGORY_LEVEL_ERROR;
+import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*;
 
 /**
  * 商品 SPU Service 实现类
@@ -59,11 +60,9 @@ public class ProductSpuServiceImpl implements ProductSpuService {
     @Override
     @Transactional(rollbackFor = Exception.class)
     public Long createSpu(ProductSpuCreateReqVO createReqVO) {
-        // 校验分类 TODO puhui999:暂不清楚为什么只能选择第三层的结点;芋艿:改成二级分类,因为商品只能放在叶子节点级别;
-        //validateCategory(createReqVO.getCategoryId());
-        // 校验品牌 TODO puhui999:暂不校验,前端没有做品牌选择;芋艿:可以加下哈
-        //brandService.validateProductBrand(createReqVO.getBrandId());
-
+        // 校验分类 TODO puhui999:暂不清楚为什么只能选择第三层的结点;芋艿:改成二级分类,因为商品只能放在叶子节点级别;fix
+        validateCategory(createReqVO.getCategoryId());
+        brandService.validateProductBrand(createReqVO.getBrandId());
         List<ProductSkuCreateOrUpdateReqVO> skuSaveReqList = createReqVO.getSkus();
         // 校验 SKU
         productSkuService.validateSkuList(skuSaveReqList, createReqVO.getSpecType());
@@ -84,10 +83,10 @@ public class ProductSpuServiceImpl implements ProductSpuService {
     public void updateSpu(ProductSpuUpdateReqVO updateReqVO) {
         // 校验 SPU 是否存在
         validateSpuExists(updateReqVO.getId());
-        // 校验分类 TODO 暂不清楚为什么只能选择第三层的结点
-        //validateCategory(updateReqVO.getCategoryId());
-        // 校验品牌 TODO 暂不校验,前端没有做品牌选择
-        //brandService.validateProductBrand(updateReqVO.getBrandId());
+        // 校验分类
+        validateCategory(updateReqVO.getCategoryId());
+        // 校验品牌
+        brandService.validateProductBrand(updateReqVO.getBrandId());
         // 校验SKU
         List<ProductSkuCreateOrUpdateReqVO> skuSaveReqList = updateReqVO.getSkus();
         productSkuService.validateSkuList(skuSaveReqList, updateReqVO.getSpecType());
@@ -103,7 +102,7 @@ public class ProductSpuServiceImpl implements ProductSpuService {
      * 基于 SKU 的信息,初始化 SPU 的信息
      * 主要是计数相关的字段,例如说市场价、最大最小价、库存等等
      *
-     * @param spu 商品 SPU
+     * @param spu  商品 SPU
      * @param skus 商品 SKU 数组
      */
     private void initSpuFromSkus(ProductSpuDO spu, List<ProductSkuCreateOrUpdateReqVO> skus) {
@@ -119,13 +118,17 @@ public class ProductSpuServiceImpl implements ProductSpuService {
         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));
+        // 若是 spu 已有状态则不处理
+        if (spu.getStatus() == null) {
+            // 默认状态为上架
+            spu.setStatus(ProductSpuStatusEnum.ENABLE.getStatus());
+            // 默认商品销量
+            spu.setSalesCount(ProductConstants.SALES_COUNT);
+            // 默认商品浏览量
+            spu.setBrowseCount(ProductConstants.BROWSE_COUNT);
+        }
     }
 
     /**
@@ -136,7 +139,7 @@ public class ProductSpuServiceImpl implements ProductSpuService {
     private void validateCategory(Long id) {
         categoryService.validateCategory(id);
         // 校验层级
-        if (categoryService.getCategoryLevel(id) != 3) {
+        if (categoryService.getCategoryLevel(id) != ProductConstants.CATEGORY_LEVEL) {
             throw exception(SPU_SAVE_FAIL_CATEGORY_LEVEL_ERROR);
         }
     }
@@ -146,6 +149,8 @@ public class ProductSpuServiceImpl implements ProductSpuService {
     public void deleteSpu(Long id) {
         // 校验存在
         validateSpuExists(id);
+        // 校验商品状态不是回收站不能删除
+        validateSpuStatus(id);
         // 删除 SPU
         productSpuMapper.deleteById(id);
         // 删除关联的 SKU
@@ -158,6 +163,19 @@ public class ProductSpuServiceImpl implements ProductSpuService {
         }
     }
 
+    /**
+     * 验证 SPU 状态是否为回收站
+     *
+     * @param id id
+     */
+    private void validateSpuStatus(Long id) {
+        ProductSpuDO spuDO = productSpuMapper.selectById(id);
+        // 判断 SPU 状态是否为回收站
+        if (ObjectUtil.notEqual(spuDO.getStatus(), ProductSpuStatusEnum.RECYCLE.getStatus())) {
+            throw exception(SPU_NOT_RECYCLE);
+        }
+    }
+
     @Override
     public ProductSpuDO getSpu(Long id) {
         return productSpuMapper.selectById(id);
@@ -173,6 +191,11 @@ public class ProductSpuServiceImpl implements ProductSpuService {
         return productSpuMapper.selectList();
     }
 
+    @Override
+    public List<ProductSpuDO> getSpuList(ProductSpuExportReqVO reqVO) {
+        return productSpuMapper.selectList(reqVO);
+    }
+
     @Override
     public PageResult<ProductSpuDO> getSpuPage(ProductSpuPageReqVO pageReqVO) {
         return productSpuMapper.selectPage(pageReqVO);
@@ -190,7 +213,6 @@ public class ProductSpuServiceImpl implements ProductSpuService {
         stockIncrCounts.forEach((id, incCount) -> productSpuMapper.updateStock(id, incCount));
     }
 
-    // TODO @puhui999:Service 尽量不做一些跟 VO 相关的拼接逻辑,目的是让 Service 更加简洁一点哈。
     @Override
     public ProductSpuDetailRespVO getSpuDetail(Long id) {
         // 获得商品 SPU
@@ -198,28 +220,9 @@ public class ProductSpuServiceImpl implements ProductSpuService {
         if (spu == null) {
             throw exception(SPU_NOT_EXISTS);
         }
-        ProductSpuDetailRespVO productSpuDetailRespVO = ProductSpuConvert.INSTANCE.convert03(spu);
         // 查询商品 SKU
         List<ProductSkuDO> skus = productSkuService.getSkuListBySpuId(spu.getId());
-        if (CollUtil.isNotEmpty(skus)){
-            // TODO @puhui999:skuVOs 更简洁一点。然后大小写要注释哈。RespVOs;因为 VO 是缩写,s 是复数
-            List<ProductSkuRespVO> skuRespVoS = ProductSkuConvert.INSTANCE.convertList(skus);
-            // 非多规格,不需要处理
-            // TODO @puhui999:统一模型,即使是单规格,也查询下,问题不大的
-            if (ObjectUtil.equal(productSpuDetailRespVO.getSpecType(), true)) {
-                // 获取所有的属性值 id
-                Set<Long> valueIds = skus.stream().flatMap(p -> p.getProperties().stream())
-                        .map(ProductSkuDO.Property::getValueId)
-                        .collect(Collectors.toSet());
-                List<ProductPropertyValueDetailRespBO> valueDetailList = productPropertyValueService.getPropertyValueDetailList(valueIds);
-                // TODO @puhui999:拼接的逻辑,最好查询好后,丢到 convert 里面统一处理;这样 Service or Controller 也可以更简洁;原则上,Controller 去组合;Service 写逻辑;Convert 转换
-                Map<Long, String> stringMap = valueDetailList.stream().collect(Collectors.toMap(ProductPropertyValueDetailRespBO::getValueId, ProductPropertyValueDetailRespBO::getValueName));
-                // 设置属性值名称
-                skuRespVoS.stream().flatMap(p -> p.getProperties().stream()).forEach(item ->item.setValueName(stringMap.get(item.getValueId())));
-            }
-            productSpuDetailRespVO.setSkus(skuRespVoS);
-        }
-        return productSpuDetailRespVO;
+        return ProductSpuConvert.INSTANCE.convertForSpuDetailRespVO(spu, skus, productPropertyValueService::getPropertyValueDetailList);
     }
 
     @Override
@@ -235,21 +238,21 @@ public class ProductSpuServiceImpl implements ProductSpuService {
 
     @Override
     public Map<Integer, Long> getTabsCount() {
-        // TODO @puhui999:map =》counts;尽量避免出现 map 这种命名,无命名含义哈
-        Map<Integer, Long> map = new HashMap<>();
+        // TODO @puhui999:map =》;尽量避免出现 map 这种命名,无命名含义哈 fix
+        Map<Integer, Long> counts = new HashMap<>(ProductConstants.SPU_TAB_COUNTS);
         // 查询销售中的商品数量
-        map.put(ProductSpuTabTypeEnum.FOR_SALE.getType(), productSpuMapper.selectCount(ProductSpuDO::getStatus, ProductSpuStatusEnum.ENABLE.getStatus()));
+        counts.put(ProductSpuPageTabEnum.FOR_SALE.getType(), productSpuMapper.selectCount(ProductSpuDO::getStatus, ProductSpuStatusEnum.ENABLE.getStatus()));
         // 查询仓库中的商品数量
-        map.put(ProductSpuTabTypeEnum.IN_WAREHOUSE.getType(),productSpuMapper.selectCount(ProductSpuDO::getStatus, ProductSpuStatusEnum.DISABLE.getStatus()));
+        counts.put(ProductSpuPageTabEnum.IN_WAREHOUSE.getType(), productSpuMapper.selectCount(ProductSpuDO::getStatus, ProductSpuStatusEnum.DISABLE.getStatus()));
         // 查询售空的商品数量
-        map.put(ProductSpuTabTypeEnum.SOLD_OUT.getType(),productSpuMapper.selectCount(ProductSpuDO::getStock, 0));
-        // 查询触发警戒库存的商品数量 TODO 警戒库存暂时为 10,后期需要使用常量或者数据库配置替换
-        // TODO @puhui999:要有空格;, productSpuMapper
-        // TODO @puhui999:Service 不要有 Mapper 的逻辑;想想咋抽象一下哈
-        map.put(ProductSpuTabTypeEnum.ALERT_STOCK.getType(),productSpuMapper.selectCount(new LambdaQueryWrapperX<ProductSpuDO>().le(ProductSpuDO::getStock, 10)));
+        counts.put(ProductSpuPageTabEnum.SOLD_OUT.getType(), productSpuMapper.selectCount(ProductSpuDO::getStock, 0));
+        // 查询触发警戒库存的商品数量
+        // TODO @puhui999:要有空格;, productSpuMapper fix
+        // TODO @puhui999:Service 不要有 Mapper 的逻辑;想想咋抽象一下哈 fix:调整为在 productSpuMapper 中书写逻辑
+        counts.put(ProductSpuPageTabEnum.ALERT_STOCK.getType(), productSpuMapper.selectCountByStockAndStatus());
         // 查询回收站中的商品数量
-        map.put(ProductSpuTabTypeEnum.RECYCLE_BIN.getType(),productSpuMapper.selectCount(ProductSpuDO::getStatus, ProductSpuStatusEnum.RECYCLE.getStatus()));
-        return map;
+        counts.put(ProductSpuPageTabEnum.RECYCLE_BIN.getType(), productSpuMapper.selectCount(ProductSpuDO::getStatus, ProductSpuStatusEnum.RECYCLE.getStatus()));
+        return counts;
     }
 
 }

+ 2 - 1
yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/category/ProductCategoryServiceImplTest.java

@@ -18,8 +18,9 @@ import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEq
 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
-import static cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO.PARENT_ID_NULL;
+
 import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.CATEGORY_NOT_EXISTS;
+import static cn.iocoder.yudao.module.product.enums.ProductConstants.PARENT_ID_NULL;
 import static org.junit.jupiter.api.Assertions.*;
 
 /**

+ 14 - 8
yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceTest.java

@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.module.product.service.sku;
 
+import cn.hutool.core.util.RandomUtil;
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
 import cn.iocoder.yudao.framework.test.core.util.AssertUtils;
@@ -49,23 +50,26 @@ public class ProductSkuServiceTest extends BaseDbUnitTest {
     @MockBean
     private ProductPropertyValueService productPropertyValueService;
 
+    public Long generateId() {
+        return RandomUtil.randomLong(100000, 999999);
+    }
+
+    public int generaInt(){return RandomUtil.randomInt(1,9999999);}
+
     @Test
     public void testUpdateSkuList() {
         // mock 数据
         ProductSkuDO sku01 = randomPojo(ProductSkuDO.class, o -> { // 测试更新
             o.setSpuId(1L);
-            //o.setProperties(singletonList(new ProductSkuDO.Property(
-            //        10L, "颜色", 20L, "红色"))); TODO 新增字段已注释
             o.setProperties(singletonList(new ProductSkuDO.Property(
-                    10L, 20L)));
+                    10L, "颜色", 20L, "红色")));
         });
         productSkuMapper.insert(sku01);
         ProductSkuDO sku02 = randomPojo(ProductSkuDO.class, o -> { // 测试删除
             o.setSpuId(1L);
-            //o.setProperties(singletonList(new ProductSkuDO.Property(
-            //        10L, "颜色", 30L, "蓝色"))); TODO 新增字段已注释
             o.setProperties(singletonList(new ProductSkuDO.Property(
-                    10L, 30L)));
+                    10L, "颜色", 30L, "蓝色")));
+
         });
         productSkuMapper.insert(sku02);
         // 准备参数
@@ -73,10 +77,12 @@ public class ProductSkuServiceTest extends BaseDbUnitTest {
         String spuName = "测试商品";
         List<ProductSkuCreateOrUpdateReqVO> skus = Arrays.asList(
                 randomPojo(ProductSkuCreateOrUpdateReqVO.class, o -> { // 测试更新
-                    o.setProperties(singletonList(new ProductSkuCreateOrUpdateReqVO.Property(10L, 20L)));
+                    o.setProperties(singletonList(new ProductSkuCreateOrUpdateReqVO.Property(
+                            10L, "颜色", 20L, "红色")));
                 }),
                 randomPojo(ProductSkuCreateOrUpdateReqVO.class, o -> { // 测试新增
-                    o.setProperties(singletonList(new ProductSkuCreateOrUpdateReqVO.Property(10L, 40L)));
+                    o.setProperties(singletonList(new ProductSkuCreateOrUpdateReqVO.Property(
+                            10L, "颜色", 20L, "红色")));
                 })
         );
 

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

@@ -6,15 +6,16 @@ import cn.hutool.core.util.RandomUtil;
 import cn.iocoder.yudao.framework.common.exception.ServiceException;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 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.ProductSpuPageRespVO;
-import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuUpdateReqVO;
+import cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategoryCreateReqVO;
+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.AppProductSpuPageReqVO;
 import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert;
+import cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO;
 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 cn.iocoder.yudao.module.product.enums.spu.ProductSpuPageTabEnum;
 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;
@@ -22,6 +23,7 @@ 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.apache.poi.ss.formula.functions.T;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
@@ -30,14 +32,19 @@ import org.springframework.boot.test.mock.mockito.MockBean;
 import org.springframework.context.annotation.Import;
 
 import javax.annotation.Resource;
+import java.math.RoundingMode;
 import java.util.*;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
+import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
 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.RandomUtils.randomPojo;
+import static org.assertj.core.util.Lists.newArrayList;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
 
 // TODO @芋艿:review 下单元测试
 
@@ -47,7 +54,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
  * @author 芋道源码
  */
 @Import(ProductSpuServiceImpl.class)
-@Disabled // TODO 芋艿:临时去掉
 public class ProductSpuServiceImplTest extends BaseDbUnitTest {
 
     @Resource
@@ -75,10 +81,35 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest {
         return RandomUtil.randomLong(100000, 999999);
     }
 
+    public int generaInt(){return RandomUtil.randomInt(1,9999999);}
+
     @Test
     public void testCreateSpu_success() {
         // 准备参数
-        ProductSpuCreateReqVO createReqVO = randomPojo(ProductSpuCreateReqVO.class);
+        ProductSkuCreateOrUpdateReqVO skuCreateOrUpdateReqVO = randomPojo(ProductSkuCreateOrUpdateReqVO.class,o->{
+            // 限制范围为正整数
+            o.setCostPrice(generaInt());
+            o.setPrice(generaInt());
+            o.setMarketPrice(generaInt());
+            o.setStock(generaInt());
+            o.setWarnStock(10);
+            o.setSubCommissionFirstPrice(generaInt());
+            o.setSubCommissionSecondPrice(generaInt());
+            // 限制分数为两位数
+            o.setWeight(RandomUtil.randomDouble(10,2, RoundingMode.HALF_UP));
+            o.setVolume(RandomUtil.randomDouble(10,2, RoundingMode.HALF_UP));
+        });
+        ProductSpuCreateReqVO createReqVO = randomPojo(ProductSpuCreateReqVO.class,o->{
+            o.setCategoryId(generateId());
+            o.setBrandId(generateId());
+            o.setUnit(RandomUtil.randomInt(1,20)); // 限制商品单位范围
+            o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围
+            o.setGiveIntegral(generaInt()); // 限制范围为正整数
+            o.setVirtualSalesCount(generaInt()); // 限制范围为正整数
+            o.setActivityOrders(newArrayList(1,3,2,4,5)); // 活动排序
+            o.setSkus(newArrayList(skuCreateOrUpdateReqVO,skuCreateOrUpdateReqVO,skuCreateOrUpdateReqVO));
+        });
+        when(categoryService.getCategoryLevel(eq(createReqVO.getCategoryId()))).thenReturn(2);
         Long spu = productSpuService.createSpu(createReqVO);
         ProductSpuDO productSpuDO = productSpuMapper.selectById(spu);
         assertPojoEquals(createReqVO, productSpuDO);
@@ -87,12 +118,55 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest {
     @Test
     public void testUpdateSpu_success() {
         // 准备参数
-        ProductSpuDO createReqVO = randomPojo(ProductSpuDO.class);
+        ProductSpuDO createReqVO = randomPojo(ProductSpuDO.class,o->{
+            o.setCategoryId(generateId());
+            o.setBrandId(generateId());
+            o.setDeliveryTemplateId(generateId());
+            o.setUnit(RandomUtil.randomInt(1,20)); // 限制商品单位范围
+            o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围
+            o.setGiveIntegral(generaInt()); // 限制范围为正整数
+            o.setVirtualSalesCount(generaInt()); // 限制范围为正整数
+            o.setActivityOrders(newArrayList(1,3,2,4,5)); // 活动排序
+            o.setPrice(generaInt()); // 限制范围为正整数
+            o.setMarketPrice(generaInt()); // 限制范围为正整数
+            o.setCostPrice(generaInt()); // 限制范围为正整数
+            o.setStock(generaInt()); // 限制范围为正整数
+            o.setGiveIntegral(generaInt()); // 限制范围为正整数
+            o.setSalesCount(generaInt()); // 限制范围为正整数
+            o.setBrowseCount(generaInt()); // 限制范围为正整数
+        });
         productSpuMapper.insert(createReqVO);
         // 准备参数
+        ProductSkuCreateOrUpdateReqVO skuCreateOrUpdateReqVO = randomPojo(ProductSkuCreateOrUpdateReqVO.class,o->{
+            // 限制范围为正整数
+            o.setCostPrice(generaInt());
+            o.setPrice(generaInt());
+            o.setMarketPrice(generaInt());
+            o.setStock(generaInt());
+            o.setWarnStock(10);
+            o.setSubCommissionFirstPrice(generaInt());
+            o.setSubCommissionSecondPrice(generaInt());
+            // 限制分数为两位数
+            o.setWeight(RandomUtil.randomDouble(10,2, RoundingMode.HALF_UP));
+            o.setVolume(RandomUtil.randomDouble(10,2, RoundingMode.HALF_UP));
+        });
+        // 准备参数
         ProductSpuUpdateReqVO reqVO = randomPojo(ProductSpuUpdateReqVO.class, o -> {
             o.setId(createReqVO.getId()); // 设置更新的 ID
+            o.setCategoryId(generateId());
+            o.setBrandId(generateId());
+            o.setUnit(RandomUtil.randomInt(1,20)); // 限制商品单位范围
+            o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围
+            o.setGiveIntegral(generaInt()); // 限制范围为正整数
+            o.setVirtualSalesCount(generaInt()); // 限制范围为正整数
+            o.setActivityOrders(newArrayList(1,3,2,4,5)); // 活动排序
+            o.setGiveIntegral(generaInt()); // 限制范围为正整数
+            o.setSalesCount(generaInt()); // 限制范围为正整数
+            o.setBrowseCount(generaInt()); // 限制范围为正整数
+            o.setStatus(0);
+            o.setSkus(newArrayList(skuCreateOrUpdateReqVO,skuCreateOrUpdateReqVO,skuCreateOrUpdateReqVO));
         });
+        when(categoryService.getCategoryLevel(eq(reqVO.getCategoryId()))).thenReturn(2);
         // 调用
         productSpuService.updateSpu(reqVO);
         // 校验是否更新正确
@@ -110,7 +184,24 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest {
     @Test
     void deleteSpu() {
         // 准备参数
-        ProductSpuDO createReqVO = randomPojo(ProductSpuDO.class);
+        ProductSpuDO createReqVO = randomPojo(ProductSpuDO.class,o->{
+            o.setCategoryId(generateId());
+            o.setBrandId(generateId());
+            o.setDeliveryTemplateId(generateId());
+            o.setUnit(RandomUtil.randomInt(1,20)); // 限制商品单位范围
+            o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围
+            o.setGiveIntegral(generaInt()); // 限制范围为正整数
+            o.setVirtualSalesCount(generaInt()); // 限制范围为正整数
+            o.setActivityOrders(newArrayList(1,3,2,4,5)); // 活动排序
+            o.setPrice(generaInt()); // 限制范围为正整数
+            o.setMarketPrice(generaInt()); // 限制范围为正整数
+            o.setCostPrice(generaInt()); // 限制范围为正整数
+            o.setStock(generaInt()); // 限制范围为正整数
+            o.setGiveIntegral(generaInt()); // 限制范围为正整数
+            o.setSalesCount(generaInt()); // 限制范围为正整数
+            o.setBrowseCount(generaInt()); // 限制范围为正整数
+            o.setStatus(-1); // 加入回收站才可删除
+        });
         productSpuMapper.insert(createReqVO);
 
         // 调用
@@ -122,7 +213,23 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest {
     @Test
     void getSpu() {
         // 准备参数
-        ProductSpuDO createReqVO = randomPojo(ProductSpuDO.class);
+        ProductSpuDO createReqVO = randomPojo(ProductSpuDO.class,o->{
+            o.setCategoryId(generateId());
+            o.setBrandId(generateId());
+            o.setDeliveryTemplateId(generateId());
+            o.setUnit(RandomUtil.randomInt(1,20)); // 限制商品单位范围
+            o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围
+            o.setGiveIntegral(generaInt()); // 限制范围为正整数
+            o.setVirtualSalesCount(generaInt()); // 限制范围为正整数
+            o.setActivityOrders(newArrayList(1,3,2,4,5)); // 活动排序
+            o.setPrice(generaInt()); // 限制范围为正整数
+            o.setMarketPrice(generaInt()); // 限制范围为正整数
+            o.setCostPrice(generaInt()); // 限制范围为正整数
+            o.setStock(generaInt()); // 限制范围为正整数
+            o.setGiveIntegral(generaInt()); // 限制范围为正整数
+            o.setSalesCount(generaInt()); // 限制范围为正整数
+            o.setBrowseCount(generaInt()); // 限制范围为正整数
+        });
         productSpuMapper.insert(createReqVO);
 
         ProductSpuDO spu = productSpuService.getSpu(createReqVO.getId());
@@ -132,19 +239,86 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest {
     @Test
     void getSpuList() {
         // 准备参数
-        ArrayList<ProductSpuDO> createReqVO = Lists.newArrayList(randomPojo(ProductSpuDO.class), randomPojo(ProductSpuDO.class));
-        productSpuMapper.insertBatch(createReqVO);
+        ArrayList<ProductSpuDO> createReqVOs = Lists.newArrayList(randomPojo(ProductSpuDO.class,o->{
+            o.setCategoryId(generateId());
+            o.setBrandId(generateId());
+            o.setDeliveryTemplateId(generateId());
+            o.setUnit(RandomUtil.randomInt(1,20)); // 限制商品单位范围
+            o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围
+            o.setGiveIntegral(generaInt()); // 限制范围为正整数
+            o.setVirtualSalesCount(generaInt()); // 限制范围为正整数
+            o.setActivityOrders(newArrayList(1,3,2,4,5)); // 活动排序
+            o.setPrice(generaInt()); // 限制范围为正整数
+            o.setMarketPrice(generaInt()); // 限制范围为正整数
+            o.setCostPrice(generaInt()); // 限制范围为正整数
+            o.setStock(generaInt()); // 限制范围为正整数
+            o.setGiveIntegral(generaInt()); // 限制范围为正整数
+            o.setSalesCount(generaInt()); // 限制范围为正整数
+            o.setBrowseCount(generaInt()); // 限制范围为正整数
+        }), randomPojo(ProductSpuDO.class,o->{
+            o.setCategoryId(generateId());
+            o.setBrandId(generateId());
+            o.setDeliveryTemplateId(generateId());
+            o.setUnit(RandomUtil.randomInt(1,20)); // 限制商品单位范围
+            o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围
+            o.setGiveIntegral(generaInt()); // 限制范围为正整数
+            o.setVirtualSalesCount(generaInt()); // 限制范围为正整数
+            o.setActivityOrders(newArrayList(1,3,2,4,5)); // 活动排序
+            o.setPrice(generaInt()); // 限制范围为正整数
+            o.setMarketPrice(generaInt()); // 限制范围为正整数
+            o.setCostPrice(generaInt()); // 限制范围为正整数
+            o.setStock(generaInt()); // 限制范围为正整数
+            o.setGiveIntegral(generaInt()); // 限制范围为正整数
+            o.setSalesCount(generaInt()); // 限制范围为正整数
+            o.setBrowseCount(generaInt()); // 限制范围为正整数
+        }));
+        productSpuMapper.insertBatch(createReqVOs);
 
         // 调用
-        List<ProductSpuDO> spuList = productSpuService.getSpuList(createReqVO.stream().map(ProductSpuDO::getId).collect(Collectors.toList()));
-        Assertions.assertIterableEquals(createReqVO, spuList);
+        List<ProductSpuDO> spuList = productSpuService.getSpuList(createReqVOs.stream().map(ProductSpuDO::getId).collect(Collectors.toList()));
+        Assertions.assertIterableEquals(createReqVOs, spuList);
     }
 
     @Test
     void getSpuPage_alarmStock_empty() {
+        // 准备参数
+        ArrayList<ProductSpuDO> createReqVOs = Lists.newArrayList(randomPojo(ProductSpuDO.class,o->{
+            o.setCategoryId(generateId());
+            o.setBrandId(generateId());
+            o.setDeliveryTemplateId(generateId());
+            o.setUnit(RandomUtil.randomInt(1,20)); // 限制商品单位范围
+            o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围
+            o.setGiveIntegral(generaInt()); // 限制范围为正整数
+            o.setVirtualSalesCount(generaInt()); // 限制范围为正整数
+            o.setActivityOrders(newArrayList(1,3,2,4,5)); // 活动排序
+            o.setPrice(generaInt()); // 限制范围为正整数
+            o.setMarketPrice(generaInt()); // 限制范围为正整数
+            o.setCostPrice(generaInt()); // 限制范围为正整数
+            o.setStock(11); // 限制范围为正整数
+            o.setGiveIntegral(generaInt()); // 限制范围为正整数
+            o.setSalesCount(generaInt()); // 限制范围为正整数
+            o.setBrowseCount(generaInt()); // 限制范围为正整数
+        }), randomPojo(ProductSpuDO.class,o->{
+            o.setCategoryId(generateId());
+            o.setBrandId(generateId());
+            o.setDeliveryTemplateId(generateId());
+            o.setUnit(RandomUtil.randomInt(1,20)); // 限制商品单位范围
+            o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围
+            o.setGiveIntegral(generaInt()); // 限制范围为正整数
+            o.setVirtualSalesCount(generaInt()); // 限制范围为正整数
+            o.setActivityOrders(newArrayList(1,3,2,4,5)); // 活动排序
+            o.setPrice(generaInt()); // 限制范围为正整数
+            o.setMarketPrice(generaInt()); // 限制范围为正整数
+            o.setCostPrice(generaInt()); // 限制范围为正整数
+            o.setStock(11); // 限制范围为正整数
+            o.setGiveIntegral(generaInt()); // 限制范围为正整数
+            o.setSalesCount(generaInt()); // 限制范围为正整数
+            o.setBrowseCount(generaInt()); // 限制范围为正整数
+        }));
+        productSpuMapper.insertBatch(createReqVOs);
         // 调用
         ProductSpuPageReqVO productSpuPageReqVO = new ProductSpuPageReqVO();
-        //productSpuPageReqVO.setAlarmStock(true);
+        productSpuPageReqVO.setTabType(ProductSpuPageTabEnum.ALERT_STOCK.getType());
 
         PageResult<ProductSpuDO> spuPage = productSpuService.getSpuPage(productSpuPageReqVO);
 
@@ -155,76 +329,67 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest {
 
     @Test
     void getSpuPage_alarmStock() {
-        // mock 数据
-        Long brandId = generateId();
-        Long categoryId = generateId();
-        String code = generateNo();
-
         // 准备参数
-        ProductSpuDO createReqVO = randomPojo(ProductSpuDO.class, o->{
-            o.setStatus(ProductSpuStatusEnum.ENABLE.getStatus());
-            //o.setTotalStock(500);
-            //o.setMinPrice(1);  // TODO ProductSpuDO中已没有相关属性
-            //o.setMaxPrice(50);
-            o.setMarketPrice(25);
-            o.setBrandId(brandId);
-            o.setCategoryId(categoryId);
-            //o.setClickCount(100);
-            //o.setCode(code); // TODO ProductSpuDO中已没有相关属性
-            o.setDescription("测试商品");
-            o.setSliderPicUrls(new ArrayList<>());
-            o.setName("测试");
-            o.setSalesCount(100);
-            //o.setSellPoint("超级加倍");
-            //o.setShowStock(true); // TODO ProductSpuDO中已没有相关属性
-            o.setVideoUrl("");
-        });
-        productSpuMapper.insert(createReqVO);
-
-        //Set<Long> alarmStockSpuIds = SetUtils.asSet(createReqVO.getId()); TODO 查询接口已改变没有使用到这个变量
-
-        List<ProductSkuDO> productSpuDOS = Arrays.asList(randomPojo(ProductSkuDO.class, o -> {
-            o.setSpuId(createReqVO.getId());
-        }), randomPojo(ProductSkuDO.class, o -> {
-            o.setSpuId(createReqVO.getId());
+        ArrayList<ProductSpuDO> createReqVOs = Lists.newArrayList(randomPojo(ProductSpuDO.class,o->{
+            o.setCategoryId(generateId());
+            o.setBrandId(generateId());
+            o.setDeliveryTemplateId(generateId());
+            o.setUnit(RandomUtil.randomInt(1,20)); // 限制商品单位范围
+            o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围
+            o.setGiveIntegral(generaInt()); // 限制范围为正整数
+            o.setVirtualSalesCount(generaInt()); // 限制范围为正整数
+            o.setActivityOrders(newArrayList(1,3,2,4,5)); // 活动排序
+            o.setPrice(generaInt()); // 限制范围为正整数
+            o.setMarketPrice(generaInt()); // 限制范围为正整数
+            o.setCostPrice(generaInt()); // 限制范围为正整数
+            o.setStock(5); // 限制范围为正整数
+            o.setGiveIntegral(generaInt()); // 限制范围为正整数
+            o.setSalesCount(generaInt()); // 限制范围为正整数
+            o.setBrowseCount(generaInt()); // 限制范围为正整数
+        }), randomPojo(ProductSpuDO.class,o->{
+            o.setCategoryId(generateId());
+            o.setBrandId(generateId());
+            o.setDeliveryTemplateId(generateId());
+            o.setUnit(RandomUtil.randomInt(1,20)); // 限制商品单位范围
+            o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围
+            o.setGiveIntegral(generaInt()); // 限制范围为正整数
+            o.setVirtualSalesCount(generaInt()); // 限制范围为正整数
+            o.setActivityOrders(newArrayList(1,3,2,4,5)); // 活动排序
+            o.setPrice(generaInt()); // 限制范围为正整数
+            o.setMarketPrice(generaInt()); // 限制范围为正整数
+            o.setCostPrice(generaInt()); // 限制范围为正整数
+            o.setStock(9); // 限制范围为正整数
+            o.setGiveIntegral(generaInt()); // 限制范围为正整数
+            o.setSalesCount(generaInt()); // 限制范围为正整数
+            o.setBrowseCount(generaInt()); // 限制范围为正整数
         }));
-
-        Mockito.when(productSkuService.getSkuListByAlarmStock()).thenReturn(productSpuDOS);
-
+        productSpuMapper.insertBatch(createReqVOs);
         // 调用
         ProductSpuPageReqVO productSpuPageReqVO = new ProductSpuPageReqVO();
-        //productSpuPageReqVO.setAlarmStock(true);
+        productSpuPageReqVO.setTabType(ProductSpuPageTabEnum.ALERT_STOCK.getType());
         PageResult<ProductSpuDO> spuPage = productSpuService.getSpuPage(productSpuPageReqVO);
-
-        PageResult<ProductSpuPageRespVO> result = ProductSpuConvert.INSTANCE.convertPage(productSpuMapper.selectPage(productSpuPageReqVO));
-        Assertions.assertIterableEquals(result.getList(), spuPage.getList());
-        assertEquals(spuPage.getTotal(), result.getTotal());
+        assertEquals(createReqVOs.size(), spuPage.getTotal());
     }
 
     @Test
-    void getSpuPage() {
-        // mock 数据
-        Long brandId = generateId();
-        Long categoryId = generateId();
-
+    void testGetSpuPage() {
         // 准备参数
-        ProductSpuDO createReqVO = randomPojo(ProductSpuDO.class, o->{
-            o.setStatus(ProductSpuStatusEnum.ENABLE.getStatus());
-            //o.setTotalStock(1);
-            //o.setMinPrice(1); // TODO ProductSpuDO中已没有相关属性
-            //o.setMaxPrice(1);
-            o.setMarketPrice(1);
-            o.setBrandId(brandId);
-            o.setSpecType(false);
-            o.setCategoryId(categoryId);
-            //o.setClickCount(1); // TODO ProductSpuDO中已没有相关属性
-            //o.setCode(generateNo());
-            o.setDescription("测试商品");
-            o.setSliderPicUrls(new ArrayList<>());
-            o.setName("测试");
-            o.setSalesCount(1);
-            //o.setSellPoint("卖点");
-            //o.setShowStock(true); // TODO ProductSpuDO中已没有相关属性
+        ProductSpuDO createReqVO = randomPojo(ProductSpuDO.class,o->{
+            o.setCategoryId(generateId());
+            o.setBrandId(generateId());
+            o.setDeliveryTemplateId(generateId());
+            o.setUnit(RandomUtil.randomInt(1,20)); // 限制商品单位范围
+            o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围
+            o.setGiveIntegral(generaInt()); // 限制范围为正整数
+            o.setVirtualSalesCount(generaInt()); // 限制范围为正整数
+            o.setActivityOrders(newArrayList(1,3,2,4,5)); // 活动排序
+            o.setPrice(generaInt()); // 限制范围为正整数
+            o.setMarketPrice(generaInt()); // 限制范围为正整数
+            o.setCostPrice(generaInt()); // 限制范围为正整数
+            o.setStock(generaInt()); // 限制范围为正整数
+            o.setGiveIntegral(generaInt()); // 限制范围为正整数
+            o.setSalesCount(generaInt()); // 限制范围为正整数
+            o.setBrowseCount(generaInt()); // 限制范围为正整数
         });
 
         // 准备参数
@@ -241,45 +406,21 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest {
 
         // 调用
         ProductSpuPageReqVO productSpuPageReqVO = new ProductSpuPageReqVO();
-        // TODO 已暂时没有相关属性,等用到时再添加
-        //productSpuPageReqVO.setAlarmStock(false);
-        //productSpuPageReqVO.setBrandId(brandId);
-        //productSpuPageReqVO.setStatus(ProductSpuStatusEnum.ENABLE.getStatus());
-        //productSpuPageReqVO.setCategoryId(categoryId);
+        // 查询条件 按需打开
+        //productSpuPageReqVO.setTabType(ProductSpuPageTabEnum.ALERT_STOCK.getType());
+        //productSpuPageReqVO.setTabType(ProductSpuPageTabEnum.RECYCLE_BIN.getType());
+        //productSpuPageReqVO.setTabType(ProductSpuPageTabEnum.FOR_SALE.getType());
+        //productSpuPageReqVO.setTabType(ProductSpuPageTabEnum.IN_WAREHOUSE.getType());
+        //productSpuPageReqVO.setTabType(ProductSpuPageTabEnum.SOLD_OUT.getType());
+        //productSpuPageReqVO.setName(createReqVO.getName());
+        //productSpuPageReqVO.setCategoryId(createReqVO.getCategoryId());
 
         PageResult<ProductSpuDO> spuPage = productSpuService.getSpuPage(productSpuPageReqVO);
 
-        PageResult<ProductSpuPageRespVO> result = ProductSpuConvert.INSTANCE.convertPage(productSpuMapper.selectPage(productSpuPageReqVO));
-        assertEquals(result, spuPage);
+        PageResult<ProductSpuRespVO> result = ProductSpuConvert.INSTANCE.convertPage(productSpuMapper.selectPage(productSpuPageReqVO));
+        assertEquals(result.getTotal(), spuPage.getTotal());
     }
 
-    @Test
-    void testGetSpuPage() {
-// 准备参数
-        ProductSpuDO createReqVO = randomPojo(ProductSpuDO.class, o -> {
-            o.setCategoryId(2L);
-        });
-        productSpuMapper.insert(createReqVO);
-
-        // 调用
-        AppProductSpuPageReqVO appSpuPageReqVO = new AppProductSpuPageReqVO();
-        appSpuPageReqVO.setCategoryId(2L);
-
-//        PageResult<AppSpuPageItemRespVO> spuPage = productSpuService.getSpuPage(appSpuPageReqVO);
-//
-//        PageResult<ProductSpuDO> result = productSpuMapper.selectPage(
-//                ProductSpuConvert.INSTANCE.convert(appSpuPageReqVO));
-//
-//        List<AppSpuPageItemRespVO> collect = result.getList()
-//                .stream()
-//                .map(ProductSpuConvert.INSTANCE::convertAppResp)
-//                .collect(Collectors.toList());
-//
-//        Assertions.assertIterableEquals(collect, spuPage.getList());
-//        assertEquals(spuPage.getTotal(), result.getTotal());
-    }
-
-
     /**
      * 生成笛卡尔积
      *
@@ -316,14 +457,48 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest {
         // 准备参数
         Map<Long, Integer> stockIncrCounts = MapUtil.builder(1L, 10).put(2L, -20).build();
         // mock 方法(数据) // TODO ProductSpuDO中已没有相关属性
-        //productSpuMapper.insert(randomPojo(ProductSpuDO.class, o -> o.setId(1L).setTotalStock(20)));
-        //productSpuMapper.insert(randomPojo(ProductSpuDO.class, o -> o.setId(2L).setTotalStock(30)));
+        productSpuMapper.insert(randomPojo(ProductSpuDO.class, o ->{
+            o.setCategoryId(generateId());
+            o.setBrandId(generateId());
+            o.setDeliveryTemplateId(generateId());
+            o.setUnit(RandomUtil.randomInt(1,20)); // 限制商品单位范围
+            o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围
+            o.setGiveIntegral(generaInt()); // 限制范围为正整数
+            o.setVirtualSalesCount(generaInt()); // 限制范围为正整数
+            o.setActivityOrders(newArrayList(1,3,2,4,5)); // 活动排序
+            o.setPrice(generaInt()); // 限制范围为正整数
+            o.setMarketPrice(generaInt()); // 限制范围为正整数
+            o.setCostPrice(generaInt()); // 限制范围为正整数
+            o.setStock(generaInt()); // 限制范围为正整数
+            o.setGiveIntegral(generaInt()); // 限制范围为正整数
+            o.setSalesCount(generaInt()); // 限制范围为正整数
+            o.setBrowseCount(generaInt()); // 限制范围为正整数
+            o.setId(1L).setStock(20);
+        }));
+        productSpuMapper.insert(randomPojo(ProductSpuDO.class, o -> {
+            o.setCategoryId(generateId());
+            o.setBrandId(generateId());
+            o.setDeliveryTemplateId(generateId());
+            o.setUnit(RandomUtil.randomInt(1,20)); // 限制商品单位范围
+            o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围
+            o.setGiveIntegral(generaInt()); // 限制范围为正整数
+            o.setVirtualSalesCount(generaInt()); // 限制范围为正整数
+            o.setActivityOrders(newArrayList(1,3,2,4,5)); // 活动排序
+            o.setPrice(generaInt()); // 限制范围为正整数
+            o.setMarketPrice(generaInt()); // 限制范围为正整数
+            o.setCostPrice(generaInt()); // 限制范围为正整数
+            o.setStock(generaInt()); // 限制范围为正整数
+            o.setGiveIntegral(generaInt()); // 限制范围为正整数
+            o.setSalesCount(generaInt()); // 限制范围为正整数
+            o.setBrowseCount(generaInt()); // 限制范围为正整数
+            o.setId(2L).setStock(30);
+        }));
 
         // 调用
         productSpuService.updateSpuStock(stockIncrCounts);
         // 断言  // TODO ProductSpuDO中已没有相关属性
-        //assertEquals(productSpuService.getSpu(1L).getTotalStock(), 30);
-        //assertEquals(productSpuService.getSpu(2L).getTotalStock(), 10);
+        assertEquals(productSpuService.getSpu(1L).getStock(), 30);
+        assertEquals(productSpuService.getSpu(2L).getStock(), 10);
     }
 
 }

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

@@ -1,5 +1,7 @@
 DELETE FROM "product_sku";
 DELETE FROM "product_spu";
-DELETE FROM "product_brand";
 DELETE FROM "product_category";
+DELETE FROM "product_brand";
+DELETE FROM "product_property";
+DELETE FROM "product_property_value";
 DELETE FROM "product_comment";

+ 114 - 70
yudao-module-mall/yudao-module-product-biz/src/test/resources/sql/create_tables.sql

@@ -1,88 +1,131 @@
 CREATE TABLE IF NOT EXISTS `product_sku` (
-    `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `id` bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
     `spu_id` bigint NOT NULL COMMENT 'spu编号',
-    `spu_name` varchar DEFAULT NULL COMMENT '商品 SPU 名字',
-    `properties` varchar DEFAULT NULL COMMENT '规格值数组-json格式, [{propertId: , valueId: }, {propertId: , valueId: }]',
-    `price` int NOT NULL DEFAULT '-1' COMMENT '销售价格,单位:分',
-    `market_price` int DEFAULT NULL COMMENT '市场价',
+    `properties` varchar(512)  DEFAULT NULL COMMENT '属性数组,JSON 格式',
+    `price` int NOT NULL DEFAULT '-1' COMMENT '商品价格,单位:分',
+    `market_price` int DEFAULT NULL COMMENT '市场价,单位:分',
     `cost_price` int NOT NULL DEFAULT '-1' COMMENT '成本价,单位: 分',
-    `pic_url` varchar NOT NULL COMMENT '图片地址',
+    `bar_code` varchar(64)  DEFAULT NULL COMMENT 'SKU 的条形码',
+    `pic_url` varchar(256)  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 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 DEFAULT NULL COMMENT '创建人',
-    `updater` varchar DEFAULT NULL COMMENT '更新人',
-    `deleted` bit(1) NOT NULL DEFAULT 0 COMMENT '是否删除',
-    PRIMARY KEY (`id`)
+    `weight` double DEFAULT NULL COMMENT '商品重量,单位:kg 千克',
+    `volume` double DEFAULT NULL COMMENT '商品体积,单位:m^3 平米',
+    `sub_commission_first_price` int DEFAULT NULL COMMENT '一级分销的佣金,单位:分',
+    `sub_commission_second_price` int DEFAULT NULL COMMENT '二级分销的佣金,单位:分',
+    `sales_count` int DEFAULT NULL COMMENT '商品销量',
+    "creator" varchar(64) DEFAULT '',
+    "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    "updater" varchar(64) DEFAULT '',
+    "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    "deleted" bit NOT NULL DEFAULT FALSE,
+    "tenant_id" bigint not null default  '0',
+    PRIMARY KEY("id")
 ) COMMENT '商品sku';
 
-
 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 '商品编码',
+    `id` bigint NOT NULL AUTO_INCREMENT COMMENT '商品 SPU 编号,自增',
     `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 '是否展示库存',
+    `keyword` varchar(256) NOT NULL COMMENT '关键字',
+    `introduction` varchar(256) NOT NULL COMMENT '商品简介',
+    `description` text NOT NULL COMMENT '商品详情',
+    `bar_code` varchar(64) NOT NULL COMMENT '条形码',
+    `category_id` bigint NOT NULL COMMENT '商品分类编号',
+    `brand_id` int DEFAULT NULL COMMENT '商品品牌编号',
+    `pic_url` varchar(256) NOT NULL COMMENT '商品封面图',
+    `slider_pic_urls` varchar(2000)  DEFAULT '' COMMENT '商品轮播图地址\n 数组,以逗号分隔\n 最多上传15张',
+    `video_url` varchar(256) DEFAULT NULL COMMENT '商品视频',
+    `unit` tinyint NOT NULL COMMENT '单位',
+    `sort` int NOT NULL DEFAULT '0' COMMENT '排序字段',
+    `status` tinyint NOT NULL COMMENT '商品状态: 0 上架(开启) 1 下架(禁用)-1 回收',
+    `spec_type` bit(1) NOT NULL COMMENT '规格类型:0 单规格 1 多规格',
+    `price` int NOT NULL DEFAULT '-1' COMMENT '商品价格,单位使用:分',
+    `market_price` int NOT NULL COMMENT '市场价,单位使用:分',
+    `cost_price` int NOT NULL DEFAULT '-1' COMMENT '成本价,单位: 分',
+    `stock` int NOT NULL DEFAULT '0' COMMENT '库存',
+    `delivery_template_id` bigint NOT NULL COMMENT '物流配置模板编号',
+    `recommend_hot` bit(1) NOT NULL COMMENT '是否热卖推荐: 0 默认 1 热卖',
+    `recommend_benefit` bit(1) NOT NULL COMMENT '是否优惠推荐: 0 默认 1 优选',
+    `recommend_best` bit(1) NOT NULL COMMENT '是否精品推荐: 0 默认 1 精品',
+    `recommend_new` bit(1) NOT NULL COMMENT '是否新品推荐: 0 默认 1 新品',
+    `recommend_good` bit(1) NOT NULL COMMENT '是否优品推荐',
+    `give_integral` int NOT NULL COMMENT '赠送积分',
+    `give_coupon_template_ids` varchar(512)  DEFAULT '' COMMENT '赠送的优惠劵编号的数组',
+    `sub_commission_type` bit(1) NOT NULL COMMENT '分销类型',
+    `activity_orders` varchar(16) NOT NULL DEFAULT '' COMMENT '活动显示排序0=默认, 1=秒杀,2=砍价,3=拼团',
     `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 DEFAULT NULL COMMENT '创建人',
-    `updater` varchar DEFAULT NULL COMMENT '更新人',
-    `deleted` bit(1) NOT NULL DEFAULT 0 COMMENT '是否删除',
-PRIMARY KEY (`id`)
+    `browse_count` int DEFAULT '0' COMMENT '商品点击量',
+    "creator" varchar(64) DEFAULT '',
+    "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    "updater" varchar(64) DEFAULT '',
+    "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    "deleted" bit NOT NULL DEFAULT FALSE,
+    "tenant_id" bigint not null default  '0',
+    PRIMARY KEY("id")
 ) COMMENT '商品spu';
 
 CREATE TABLE IF NOT EXISTS `product_category` (
     `id` bigint NOT NULL AUTO_INCREMENT COMMENT '分类编号',
-    `parent_id` bigint DEFAULT NULL COMMENT '父分类编号',
-    `name` varchar(128) NOT NULL COMMENT '分类名称',
-    `pic_url` varchar DEFAULT NULL COMMENT '分类图片',
-    `big_pic_url` varchar DEFAULT NULL COMMENT 'PC端分类图',
-    `sort` int NOT NULL DEFAULT '0' COMMENT '排序字段',
-    `status` bit(1) DEFAULT NULL COMMENT '状态',
-    `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-    `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-    `creator` varchar DEFAULT NULL COMMENT '创建人',
-    `updater` varchar DEFAULT NULL COMMENT '更新人',
-    `deleted` bit(1) NOT NULL DEFAULT 0 COMMENT '是否删除',
-    PRIMARY KEY (`id`)
+    `parent_id` bigint NOT NULL COMMENT '父分类编号',
+    `name` varchar(255) NOT NULL COMMENT '分类名称',
+    `pic_url` varchar(255) NOT NULL COMMENT '移动端分类图',
+    `big_pic_url` varchar(255) DEFAULT NULL COMMENT 'PC 端分类图',
+    `sort` int DEFAULT '0' COMMENT '分类排序',
+    `status` tinyint NOT NULL COMMENT '开启状态',
+    "creator" varchar(64) DEFAULT '',
+    "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    "updater" varchar(64) DEFAULT '',
+    "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    "deleted" bit NOT NULL DEFAULT FALSE,
+    "tenant_id" bigint not null default  '0',
+    PRIMARY KEY("id")
 ) COMMENT '商品分类';
 
 CREATE TABLE IF NOT EXISTS `product_brand` (
     `id` bigint NOT NULL AUTO_INCREMENT COMMENT '品牌编号',
-    `name` varchar(128) NOT NULL COMMENT '品牌名称',
-    `pic_url` varchar DEFAULT NULL COMMENT '品牌图片',
-    `sort` int NOT NULL DEFAULT '0' COMMENT '排序字段',
-    `description` varchar(256) NOT NULL DEFAULT '0' COMMENT '品牌描述',
-    `status` bit(1) DEFAULT NULL COMMENT '状态',
-    `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-    `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-    `creator` varchar DEFAULT NULL COMMENT '创建人',
-    `updater` varchar DEFAULT NULL COMMENT '更新人',
-    `deleted` bit(1) NOT NULL DEFAULT 0 COMMENT '是否删除',
-    PRIMARY KEY (`id`)
+    `name` varchar(255) NOT NULL COMMENT '品牌名称',
+    `pic_url` varchar(255) NOT NULL COMMENT '品牌图片',
+    `sort` int DEFAULT '0' COMMENT '品牌排序',
+    `description` varchar(1024) DEFAULT NULL COMMENT '品牌描述',
+    `status` tinyint NOT NULL COMMENT '状态',
+    "creator" varchar(64) DEFAULT '',
+    "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    "updater" varchar(64) DEFAULT '',
+    "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    "deleted" bit NOT NULL DEFAULT FALSE,
+    "tenant_id" bigint not null default  '0',
+    PRIMARY KEY("id")
 ) COMMENT '商品品牌';
 
+CREATE TABLE IF NOT EXISTS `product_property` (
+    `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `name` varchar(64) DEFAULT NULL COMMENT '规格名称',
+    `status` tinyint DEFAULT NULL COMMENT '状态: 0 开启 ,1 禁用',
+    "creator" varchar(64) DEFAULT '',
+    "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    "updater" varchar(64) DEFAULT '',
+    "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    "deleted" bit NOT NULL DEFAULT FALSE,
+    "tenant_id" bigint not null default  '0',
+    `remark` varchar(255) DEFAULT NULL COMMENT '备注',
+    PRIMARY KEY("id")
+) COMMENT '规格名称';
+
+CREATE TABLE IF NOT EXISTS `product_property_value` (
+    `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `property_id` bigint DEFAULT NULL COMMENT '规格键id',
+    `name` varchar(128) DEFAULT NULL COMMENT '规格值名字',
+    `status` tinyint DEFAULT NULL COMMENT '状态: 1 开启 ,2 禁用',
+    "creator" varchar(64) DEFAULT '',
+    "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    "updater" varchar(64) DEFAULT '',
+    "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    "deleted" bit NOT NULL DEFAULT FALSE,
+    "tenant_id" bigint not null default  '0',
+    `remark` varchar(255) DEFAULT NULL COMMENT '备注',
+    PRIMARY KEY("id")
+) COMMENT '规格值';
+
 CREATE TABLE IF NOT EXISTS `product_comment` (
     `id` bigint NOT NULL AUTO_INCREMENT COMMENT '评价编号',
     `user_id` bigint NOT NULL COMMENT ' 评价ID 用户编号',
@@ -108,10 +151,11 @@ CREATE TABLE IF NOT EXISTS `product_comment` (
     `additional_content` varchar(2000) COMMENT '追加评价内容',
     `additional_pic_urls` varchar(1024) COMMENT '追评评价图片地址数组,以逗号分隔最多上传9张',
     `additional_time` datetime COMMENT '追加评价时间',
-    `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-    `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-    `creator` varchar DEFAULT NULL COMMENT '创建人',
-    `updater` varchar DEFAULT NULL COMMENT '更新人',
-    `deleted` bit(1) NOT NULL DEFAULT 0 COMMENT '是否删除',
+    "creator" varchar(64) DEFAULT '',
+    "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    "updater" varchar(64) DEFAULT '',
+    "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    "deleted" bit NOT NULL DEFAULT FALSE,
+    "tenant_id" bigint not null default  '0',
     PRIMARY KEY (`id`)
 ) COMMENT '商品评价';