diff --git a/order/order-biz/src/main/java/cn/iocoder/mall/order/biz/dto/order/OrderCreateDTO.java b/order/order-biz/src/main/java/cn/iocoder/mall/order/biz/dto/order/OrderCreateDTO.java
index 543b09f0f..b9145d2ae 100644
--- a/order/order-biz/src/main/java/cn/iocoder/mall/order/biz/dto/order/OrderCreateDTO.java
+++ b/order/order-biz/src/main/java/cn/iocoder/mall/order/biz/dto/order/OrderCreateDTO.java
@@ -16,6 +16,7 @@ import java.util.List;
*/
@Data
@Accessors(chain = true)
+// TODO FROM 芋艿 to xiaofeng:辛苦后续补充下 Validation 注解哈
public class OrderCreateDTO implements Serializable {
/**
diff --git a/order/order-rest/src/main/java/cn/iocoder/mall/order/rest/request/admin/OrderPageRequest.java b/order/order-rest/src/main/java/cn/iocoder/mall/order/rest/request/admin/OrderPageRequest.java
index 580029ec1..412e446a5 100644
--- a/order/order-rest/src/main/java/cn/iocoder/mall/order/rest/request/admin/OrderPageRequest.java
+++ b/order/order-rest/src/main/java/cn/iocoder/mall/order/rest/request/admin/OrderPageRequest.java
@@ -3,11 +3,12 @@ package cn.iocoder.mall.order.rest.request.admin;
import cn.iocoder.mall.order.biz.bo.order.OrderBO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
-import java.io.Serializable;
-import java.util.List;
import lombok.Data;
import lombok.experimental.Accessors;
+import java.io.Serializable;
+import java.util.List;
+
/**
* 订单分页 vo
*
@@ -17,6 +18,7 @@ import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
@ApiModel("订单VO")
+// TODO FROM 芋艿 to jwf1173:命名要改下,rest 以 Admins 和 Users 开头
public class OrderPageRequest implements Serializable {
/**
diff --git a/product/pom.xml b/product/pom.xml
index 960d54248..eaf283845 100644
--- a/product/pom.xml
+++ b/product/pom.xml
@@ -20,6 +20,7 @@
product-rest
product-biz
product-biz-api
+ product-mq
diff --git a/product/product-biz-api/src/main/java/cn/iocoder/mall/product/biz/enums/category/ProductCategoryConstants.java b/product/product-biz-api/src/main/java/cn/iocoder/mall/product/biz/enums/category/ProductCategoryConstants.java
index 0613d0b76..4039f3cdf 100644
--- a/product/product-biz-api/src/main/java/cn/iocoder/mall/product/biz/enums/category/ProductCategoryConstants.java
+++ b/product/product-biz-api/src/main/java/cn/iocoder/mall/product/biz/enums/category/ProductCategoryConstants.java
@@ -1,10 +1,19 @@
package cn.iocoder.mall.product.biz.enums.category;
-public class ProductCategoryConstants {
+public interface ProductCategoryConstants {
+
+ /**
+ * 状态 - 开启
+ */
+ Integer STATUS_ENABLE = 1;
+ /**
+ * 状态 - 关闭
+ */
+ Integer STATUS_DISABLE = 2;
/**
* 父分类编号 - 根节点
*/
- public static final Integer PID_ROOT = 0;
+ Integer PID_ROOT = 0;
}
\ No newline at end of file
diff --git a/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/convert/product/ProductSpuConvert.java b/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/convert/sku/ProductSpuConvert.java
similarity index 93%
rename from product/product-biz/src/main/java/cn/iocoder/mall/product/biz/convert/product/ProductSpuConvert.java
rename to product/product-biz/src/main/java/cn/iocoder/mall/product/biz/convert/sku/ProductSpuConvert.java
index bc4f871d7..624980de3 100644
--- a/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/convert/product/ProductSpuConvert.java
+++ b/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/convert/sku/ProductSpuConvert.java
@@ -1,13 +1,13 @@
-package cn.iocoder.mall.product.biz.convert.product;
+package cn.iocoder.mall.product.biz.convert.sku;
import cn.iocoder.common.framework.util.StringUtil;
import cn.iocoder.mall.product.biz.bo.product.*;
import cn.iocoder.mall.product.biz.dataobject.category.ProductCategoryDO;
import cn.iocoder.mall.product.biz.dataobject.spu.ProductSkuDO;
import cn.iocoder.mall.product.biz.dataobject.spu.ProductSpuDO;
-import cn.iocoder.mall.product.biz.dto.product.ProductSkuAddOrUpdateDTO;
-import cn.iocoder.mall.product.biz.dto.product.ProductSpuAddDTO;
-import cn.iocoder.mall.product.biz.dto.product.ProductSpuUpdateDTO;
+import cn.iocoder.mall.product.biz.dto.sku.ProductSkuAddOrUpdateDTO;
+import cn.iocoder.mall.product.biz.dto.sku.ProductSpuAddDTO;
+import cn.iocoder.mall.product.biz.dto.sku.ProductSpuUpdateDTO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
@@ -24,6 +24,17 @@ public interface ProductSpuConvert {
ProductSpuConvert INSTANCE = Mappers.getMapper(ProductSpuConvert.class);
+ @Mappings({
+ @Mapping(source = "picUrls", target = "picUrls", ignore = true)
+ })
+ ProductSpuDO convertToSpuDO(ProductSpuAddDTO productSpuAddDTO);
+
+
+ @Mappings({
+ @Mapping(source = "attrs", target = "attrs", ignore = true)
+ })
+ ProductSkuDO convertToSkuDO(ProductSkuAddOrUpdateDTO productSkuAddDTO);
+
@Mappings({
@Mapping(source = "picUrls", target = "picUrls", qualifiedByName = "translatePicUrlsFromString")
})
@@ -37,16 +48,6 @@ public interface ProductSpuConvert {
@Mappings({})
List convert(List spus);
- @Mappings({
- @Mapping(source = "picUrls", target = "picUrls", ignore = true)
- })
- ProductSpuDO convert(ProductSpuAddDTO productSpuAddDTO);
-
- @Mappings({
- @Mapping(source = "attrs", target = "attrs", ignore = true)
- })
- ProductSkuDO convert(ProductSkuAddOrUpdateDTO productSkuAddDTO);
-
@Mappings({
@Mapping(source = "picUrls", target = "picUrls", ignore = true)
diff --git a/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/dao/spu/ProductSkuMapper.java b/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/dao/sku/ProductSkuMapper.java
similarity index 79%
rename from product/product-biz/src/main/java/cn/iocoder/mall/product/biz/dao/spu/ProductSkuMapper.java
rename to product/product-biz/src/main/java/cn/iocoder/mall/product/biz/dao/sku/ProductSkuMapper.java
index da316d205..9a2179205 100644
--- a/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/dao/spu/ProductSkuMapper.java
+++ b/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/dao/sku/ProductSkuMapper.java
@@ -1,8 +1,9 @@
-package cn.iocoder.mall.product.biz.dao.spu;
+package cn.iocoder.mall.product.biz.dao.sku;
import cn.iocoder.mall.product.biz.dataobject.spu.ProductSkuDO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@@ -16,4 +17,6 @@ public interface ProductSkuMapper extends BaseMapper {
.eq(ProductSkuDO::getStatus, status)
.eq(ProductSkuDO::getDeleted, false));
}
+
+ void insertList(@Param("productSkuDOs") List productSkuDOs);
}
diff --git a/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/dao/spu/ProductSpuMapper.java b/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/dao/sku/ProductSpuMapper.java
similarity index 85%
rename from product/product-biz/src/main/java/cn/iocoder/mall/product/biz/dao/spu/ProductSpuMapper.java
rename to product/product-biz/src/main/java/cn/iocoder/mall/product/biz/dao/sku/ProductSpuMapper.java
index 3e21a2d0e..4b001141c 100644
--- a/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/dao/spu/ProductSpuMapper.java
+++ b/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/dao/sku/ProductSpuMapper.java
@@ -1,4 +1,4 @@
-package cn.iocoder.mall.product.biz.dao.spu;
+package cn.iocoder.mall.product.biz.dao.sku;
import cn.iocoder.mall.product.biz.dataobject.spu.ProductSpuDO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
diff --git a/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/dao/spu/UserProductSpuCollectionsMapper.java b/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/dao/sku/UserProductSpuCollectionsMapper.java
similarity index 97%
rename from product/product-biz/src/main/java/cn/iocoder/mall/product/biz/dao/spu/UserProductSpuCollectionsMapper.java
rename to product/product-biz/src/main/java/cn/iocoder/mall/product/biz/dao/sku/UserProductSpuCollectionsMapper.java
index 9e1037d2b..ca604ec7e 100644
--- a/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/dao/spu/UserProductSpuCollectionsMapper.java
+++ b/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/dao/sku/UserProductSpuCollectionsMapper.java
@@ -1,4 +1,4 @@
-package cn.iocoder.mall.product.biz.dao.spu;
+package cn.iocoder.mall.product.biz.dao.sku;
import cn.iocoder.mall.product.biz.dataobject.spu.UserProductSpuCollectionsDO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
diff --git a/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/dto/sku/ProductSkuAddOrUpdateDTO.java b/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/dto/sku/ProductSkuAddOrUpdateDTO.java
new file mode 100644
index 000000000..42a88e8b3
--- /dev/null
+++ b/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/dto/sku/ProductSkuAddOrUpdateDTO.java
@@ -0,0 +1,35 @@
+package cn.iocoder.mall.product.biz.dto.sku;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+/**
+ * 商品 Sku 添加 DTO
+ */
+@Data
+@Accessors(chain = true)
+public class ProductSkuAddOrUpdateDTO {
+
+ /**
+ * 规格值数组
+ */
+ @NotNull(message = "规格值数组不能为空")
+ private List attrs;
+ /**
+ * 价格,单位:分
+ */
+ @NotNull(message = "价格不能为空")
+ @Min(value = 1L, message = "最小价格为 1")
+ private Integer price;
+ /**
+ * 库存数量
+ */
+ @NotNull(message = "库存数量不能为空")
+ @Min(value = 1L, message = "最小库存为 1")
+ private Integer quantity;
+
+}
diff --git a/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/dto/sku/ProductSpuAddDTO.java b/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/dto/sku/ProductSpuAddDTO.java
new file mode 100644
index 000000000..d4e0455d1
--- /dev/null
+++ b/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/dto/sku/ProductSpuAddDTO.java
@@ -0,0 +1,62 @@
+package cn.iocoder.mall.product.biz.dto.sku;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+/**
+ * 商品 SPU + SKU 添加 DTO
+ */
+@Data
+@Accessors(chain = true)
+public class ProductSpuAddDTO {
+
+ // ========== 基本信息 =========
+ /**
+ * SPU 名字
+ */
+ @NotEmpty(message = "SPU 名字不能为空")
+ private String name;
+ /**
+ * 卖点
+ */
+ @NotEmpty(message = "卖点不能为空")
+ private String sellPoint;
+ /**
+ * 描述
+ */
+ @NotEmpty(message = "描述不能为空")
+ private String description;
+ /**
+ * 分类编号
+ */
+ @NotNull(message = "分类不能为空")
+ private Integer cid;
+ /**
+ * 商品主图地址
+ */
+ @NotNull(message = "商品主图不能为空")
+ private List picUrls;
+
+ // ========== 其他信息 =========
+ /**
+ * 是否上架商品(是否可见)。
+ *
+ * true 为已上架
+ * false 为已下架
+ */
+ @NotNull(message = "是否上架不能为空")
+ private Boolean visible;
+
+ // ========== SKU =========
+
+ /**
+ * SKU 数组
+ */
+ @NotNull(message = "SKU 不能为空")
+ private List skus;
+
+}
diff --git a/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/dto/sku/ProductSpuUpdateDTO.java b/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/dto/sku/ProductSpuUpdateDTO.java
new file mode 100644
index 000000000..1bf191478
--- /dev/null
+++ b/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/dto/sku/ProductSpuUpdateDTO.java
@@ -0,0 +1,69 @@
+package cn.iocoder.mall.product.biz.dto.sku;
+
+import cn.iocoder.mall.product.biz.dto.product.ProductSkuAddOrUpdateDTO;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+/**
+ * 商品 SPU + SKU 更新 DTO
+ */
+@Data
+@Accessors(chain = true)
+public class ProductSpuUpdateDTO {
+
+ /**
+ * Spu 编号
+ */
+ @NotNull(message = "SPU 编号不能为空")
+ private Integer id;
+
+ // ========== 基本信息 =========
+ /**
+ * SPU 名字
+ */
+ @NotEmpty(message = "SPU 名字不能为空")
+ private String name;
+ /**
+ * 卖点
+ */
+ @NotEmpty(message = "卖点不能为空")
+ private String sellPoint;
+ /**
+ * 描述
+ */
+ @NotEmpty(message = "描述不能为空")
+ private String description;
+ /**
+ * 分类编号
+ */
+ @NotNull(message = "分类不能为空")
+ private Integer cid;
+ /**
+ * 商品主图地址
+ */
+ @NotNull(message = "商品主图不能为空")
+ private List picUrls;
+
+ // ========== 其他信息 =========
+ /**
+ * 是否上架商品(是否可见)。
+ *
+ * true 为已上架
+ * false 为已下架
+ */
+ @NotNull(message = "是否上架不能为空")
+ private Boolean visible;
+
+ // ========== SKU =========
+
+ /**
+ * SKU 数组
+ */
+ @NotNull(message = "SKU 不能为空")
+ private List skus;
+
+}
diff --git a/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/service/category/ProductCategoryService.java b/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/service/category/ProductCategoryService.java
index 2590d78bc..124b4f00e 100644
--- a/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/service/category/ProductCategoryService.java
+++ b/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/service/category/ProductCategoryService.java
@@ -1,6 +1,7 @@
package cn.iocoder.mall.product.biz.service.category;
import cn.iocoder.mall.product.biz.bo.category.ProductCategoryBO;
+import cn.iocoder.mall.product.biz.dataobject.category.ProductCategoryDO;
import cn.iocoder.mall.product.biz.dto.category.ProductCategoryAddDTO;
import cn.iocoder.mall.product.biz.dto.category.ProductCategoryDeleteDTO;
import cn.iocoder.mall.product.biz.dto.category.ProductCategoryUpdateDTO;
@@ -21,12 +22,14 @@ public interface ProductCategoryService {
/**
* 获取所有商品分类
+ *
* @return
*/
List getAllProductCategory();
/**
* 新增商品分类
+ *
* @param productCategoryAddDTO
* @return
*/
@@ -34,6 +37,7 @@ public interface ProductCategoryService {
/**
* 更新商品分类
+ *
* @param productCategoryUpdateDTO
* @return
*/
@@ -41,6 +45,7 @@ public interface ProductCategoryService {
/**
* 更新商品分类状态
+ *
* @param productCategoryUpdateStatusDTO
* @return
*/
@@ -48,8 +53,17 @@ public interface ProductCategoryService {
/**
* 删除商品分类
+ *
* @param productCategoryDeleteDTO
* @return
*/
Boolean deleteProductCategory(@Valid ProductCategoryDeleteDTO productCategoryDeleteDTO);
+
+ /**
+ * 校验分类是否可用
+ *
+ * @param productCategoryId 分类ID
+ * @return 商品分类
+ */
+ ProductCategoryDO validProductCategory(Integer productCategoryId);
}
diff --git a/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/service/category/ProductCategoryServiceImpl.java b/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/service/category/ProductCategoryServiceImpl.java
index 31e34bcef..c540ab8fb 100644
--- a/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/service/category/ProductCategoryServiceImpl.java
+++ b/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/service/category/ProductCategoryServiceImpl.java
@@ -10,11 +10,13 @@ import cn.iocoder.mall.product.biz.dto.category.ProductCategoryAddDTO;
import cn.iocoder.mall.product.biz.dto.category.ProductCategoryDeleteDTO;
import cn.iocoder.mall.product.biz.dto.category.ProductCategoryUpdateDTO;
import cn.iocoder.mall.product.biz.dto.category.ProductCategoryUpdateStatusDTO;
+import cn.iocoder.mall.product.biz.enums.ProductErrorCodeEnum;
import cn.iocoder.mall.product.biz.enums.category.ProductCategoryConstants;
import cn.iocoder.mall.product.biz.enums.category.ProductCategoryStatusEnum;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
+
import java.util.Arrays;
import java.util.Date;
import java.util.List;
@@ -32,21 +34,12 @@ public class ProductCategoryServiceImpl implements ProductCategoryService {
@Autowired
private ProductCategoryMapper productCategoryMapper;
- /**
- * 获取所有商品分类
- * @return
- */
@Override
public List getAllProductCategory() {
List categoryList = productCategoryMapper.selectList(null);
return ProductCategoryConvert.INSTANCE.convertToAllListBO(categoryList);
}
- /**
- * 新增商品分类
- * @param productCategoryAddDTO
- * @return
- */
@Override
public ProductCategoryBO addProductCategory(ProductCategoryAddDTO productCategoryAddDTO) {
// 校验父分类
@@ -62,11 +55,6 @@ public class ProductCategoryServiceImpl implements ProductCategoryService {
return ProductCategoryConvert.INSTANCE.convertToBO(productCategory);
}
- /**
- * 更新商品分类
- * @param productCategoryUpdateDTO
- * @return
- */
@Override
public Boolean updateProductCategory(ProductCategoryUpdateDTO productCategoryUpdateDTO) {
// 校验当前分类是否存在
@@ -91,11 +79,6 @@ public class ProductCategoryServiceImpl implements ProductCategoryService {
return true;
}
- /**
- * 更新商品分类状态
- * @param productCategoryUpdateStatusDTO
- * @return
- */
@Override
public Boolean updateProductCategoryStatus(ProductCategoryUpdateStatusDTO productCategoryUpdateStatusDTO) {
// 校验商品分类是否存在
@@ -118,11 +101,6 @@ public class ProductCategoryServiceImpl implements ProductCategoryService {
return true;
}
- /**
- * 删除商品分类
- * @param productCategoryDeleteDTO
- * @return
- */
@Override
public Boolean deleteProductCategory(ProductCategoryDeleteDTO productCategoryDeleteDTO) {
Integer productCategoryId = productCategoryDeleteDTO.getId();
@@ -149,10 +127,6 @@ public class ProductCategoryServiceImpl implements ProductCategoryService {
return true;
}
- /**
- * 校验商品分类的父分类
- * @param pid
- */
private void validParent(Integer pid) {
if (!ProductCategoryConstants.PID_ROOT.equals(pid)) {
ProductCategoryDO parentCategory = productCategoryMapper.selectById(pid);
@@ -167,4 +141,18 @@ public class ProductCategoryServiceImpl implements ProductCategoryService {
}
}
+ @Override
+ public ProductCategoryDO validProductCategory(Integer productCategoryId) {
+ // 校验分类是否存在
+ ProductCategoryDO productCategory = productCategoryMapper.selectById(productCategoryId);
+ if (productCategory == null) {
+ throw ServiceExceptionUtil.exception(ProductErrorCodeEnum.PRODUCT_CATEGORY_NOT_EXISTS.getCode());
+ }
+ // 只有禁用的商品分类才可以删除
+ if (ProductCategoryConstants.STATUS_DISABLE.equals(productCategory.getStatus())) {
+ throw ServiceExceptionUtil.exception(ProductErrorCodeEnum.PRODUCT_CATEGORY_MUST_ENABLE.getCode());
+ }
+ // 返回结果
+ return productCategory;
+ }
}
diff --git a/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/service/spu/ProductSpuServiceImpl.java b/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/service/spu/ProductSpuServiceImpl.java
index c4f111329..a03d39452 100644
--- a/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/service/spu/ProductSpuServiceImpl.java
+++ b/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/service/spu/ProductSpuServiceImpl.java
@@ -2,25 +2,30 @@ package cn.iocoder.mall.product.biz.service.spu;
import cn.iocoder.common.framework.util.ServiceExceptionUtil;
import cn.iocoder.common.framework.util.StringUtil;
+import cn.iocoder.mall.mybatis.enums.DeletedStatusEnum;
import cn.iocoder.mall.product.biz.bo.product.ProductAttrAndValuePairBO;
import cn.iocoder.mall.product.biz.bo.product.ProductSpuDetailBO;
-import cn.iocoder.mall.product.biz.convert.product.ProductSpuConvert;
+import cn.iocoder.mall.product.biz.convert.sku.ProductSpuConvert;
import cn.iocoder.mall.product.biz.dao.category.ProductCategoryMapper;
-import cn.iocoder.mall.product.biz.dao.spu.ProductSkuMapper;
-import cn.iocoder.mall.product.biz.dao.spu.ProductSpuMapper;
+import cn.iocoder.mall.product.biz.dao.sku.ProductSkuMapper;
+import cn.iocoder.mall.product.biz.dao.sku.ProductSpuMapper;
import cn.iocoder.mall.product.biz.dataobject.category.ProductCategoryDO;
import cn.iocoder.mall.product.biz.dataobject.spu.ProductSkuDO;
import cn.iocoder.mall.product.biz.dataobject.spu.ProductSpuDO;
+import cn.iocoder.mall.product.biz.dto.sku.ProductSkuAddOrUpdateDTO;
+import cn.iocoder.mall.product.biz.dto.sku.ProductSpuAddDTO;
import cn.iocoder.mall.product.biz.enums.ProductErrorCodeEnum;
+import cn.iocoder.mall.product.biz.enums.category.ProductCategoryConstants;
import cn.iocoder.mall.product.biz.enums.spu.ProductSpuConstants;
import cn.iocoder.mall.product.biz.service.attr.ProductAttrService;
+import cn.iocoder.mall.product.biz.service.category.ProductCategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
+import java.util.stream.Collectors;
@Service
public class ProductSpuServiceImpl implements ProductSpuService {
@@ -33,6 +38,8 @@ public class ProductSpuServiceImpl implements ProductSpuService {
private ProductCategoryMapper productCategoryMapper;
@Autowired
private ProductAttrService productAttrService;
+ @Autowired
+ private ProductCategoryService productCategoryService;
@Override
public ProductSpuDetailBO getProductSpuDetail(Integer spuId) {
@@ -55,4 +62,109 @@ public class ProductSpuServiceImpl implements ProductSpuService {
return ProductSpuConvert.INSTANCE.convert2(spu, skus, attrAndValuePairList, category);
}
+ public ProductSpuDetailBO addProductSpu(Integer adminId, ProductSpuAddDTO productSpuAddDTO) {
+ ProductSpuDetailBO productSpuDetailBO = addProductSpu0(adminId, productSpuAddDTO);
+ // 如果新增生成,发送创建商品 Topic 消息
+ // TODO 芋艿,先不考虑事务的问题。等后面的 fescar 一起搞
+// sendProductUpdateMessage(productSpuDetailBO.getId());
+ // 返回成功
+ return productSpuDetailBO;
+ }
+
+ @SuppressWarnings("Duplicates")
+ @Transactional
+ public ProductSpuDetailBO addProductSpu0(Integer adminId, ProductSpuAddDTO productSpuAddDTO) {
+ // 校验商品分类分类存在
+ ProductCategoryDO category = productCategoryService.validProductCategory(productSpuAddDTO.getCid());
+ if (ProductCategoryConstants.PID_ROOT.equals(category.getPid())) {
+ // 商品只能添加到二级分类下
+ throw ServiceExceptionUtil.exception(ProductErrorCodeEnum.PRODUCT_SPU_CATEGORY_MUST_BE_LEVEL2.getCode());
+ }
+ // 校验规格是否存在
+ Set productAttrValueIds = new HashSet<>();
+ productSpuAddDTO.getSkus().forEach(productSkuAddDTO -> productAttrValueIds.addAll(productSkuAddDTO.getAttrs()));
+ // 读取规格时,需要考虑规格是否被禁用
+ List attrAndValuePairList = productAttrService.validProductAttrAndValue(productAttrValueIds, true);
+ // 保存 Spu
+ ProductSpuDO spu = ProductSpuConvert.INSTANCE.convertToSpuDO(productSpuAddDTO)
+ .setPicUrls(StringUtil.join(productSpuAddDTO.getPicUrls(), ","))
+ .setSort(0); // 排序为 0
+ spu.setCreateTime(new Date());
+ spu.setDeleted(DeletedStatusEnum.DELETED_NO.getValue());
+ // 初始化 sku 相关信息到 spu 中
+ initSpuFromSkus(spu, productSpuAddDTO.getSkus());
+ productSpuMapper.insert(spu);
+ // 保存 Sku
+ List skus = productSpuAddDTO.getSkus().stream().map(productSkuAddDTO -> {
+ ProductSkuDO sku = ProductSpuConvert.INSTANCE.convertToSkuDO(productSkuAddDTO)
+ .setSpuId(spu.getId())
+ .setStatus(ProductSpuConstants.SKU_STATUS_ENABLE)
+ .setAttrs(StringUtil.join(productSkuAddDTO.getAttrs(), ","));
+ sku.setCreateTime(new Date());
+ sku.setDeleted(DeletedStatusEnum.DELETED_NO.getValue());
+ return sku;
+ }).collect(Collectors.toList());
+ // 校验 Sku 规格
+ validProductSku(productSpuAddDTO.getSkus(), attrAndValuePairList);
+ // 插入 SKU 到数据库
+ productSkuMapper.insertList(skus);
+ // 返回成功
+ return ProductSpuConvert.INSTANCE.convert2(spu, skus, attrAndValuePairList, category);
+ }
+
+ /**
+ * 根据 sku 数组,计算相关的字段到 spu 中。
+ *
+ * @param spu spu
+ * @param skus sku 数组
+ */
+ private void initSpuFromSkus(ProductSpuDO spu, List skus) {
+ assert skus.size() > 0; // 写个断言,避免下面警告
+ spu.setPrice(skus.stream().min(Comparator.comparing(ProductSkuAddOrUpdateDTO::getPrice)).get().getPrice()); // 求最小价格
+ spu.setQuantity(skus.stream().mapToInt(ProductSkuAddOrUpdateDTO::getQuantity).sum()); // 求库存之和
+ }
+
+// private boolean sendProductUpdateMessage(Integer id) {
+// // 创建 Message 对象
+// ProductUpdateMessage message = new ProductUpdateMessage().setId(id);
+// // 创建 Spring Message 对象
+// Message springMessage = MessageBuilder.withPayload(message)
+// .build();
+// // 发送消息
+// return mqStreamProducer.productUpdateOutput().send(springMessage);
+// }
+
+ /**
+ * 校验 sku 是否合法
+ *
+ * @param productSkuAddDTOs sku 添加或修改信息
+ * @param productAttrDetailBOs 商品规格明细数组
+ */
+ private void validProductSku(List productSkuAddDTOs, List productAttrDetailBOs) {
+ // 创建 ProductAttrDetailBO 的映射。其中,KEY 为 ProductAttrDetailBO.attrValueId ,即规格值的编号
+ Map productAttrDetailBOMap = productAttrDetailBOs.stream().collect(
+ Collectors.toMap(ProductAttrAndValuePairBO::getAttrValueId, productAttrDetailBO -> productAttrDetailBO));
+ // 1. 先校验,一个 Sku 下,没有重复的规格。校验方式是,遍历每个 Sku ,看看是否有重复的规格 attrId
+ for (ProductSkuAddOrUpdateDTO sku : productSkuAddDTOs) {
+ Set attrIds = sku.getAttrs().stream().map(attrValueId -> productAttrDetailBOMap.get(attrValueId).getAttrId())
+ .collect(Collectors.toSet());
+ if (attrIds.size() != sku.getAttrs().size()) {
+ throw ServiceExceptionUtil.exception(ProductErrorCodeEnum.PRODUCT_SKU_ATTR_CANT_NOT_DUPLICATE.getCode());
+ }
+ }
+ // 2. 再校验,每个 Sku 的规格值的数量,是一致的。
+ int attrSize = productSkuAddDTOs.get(0).getAttrs().size();
+ for (int i = 1; i < productSkuAddDTOs.size(); i++) {
+ if (attrSize != productSkuAddDTOs.get(i).getAttrs().size()) {
+ throw ServiceExceptionUtil.exception(ProductErrorCodeEnum.PRODUCT_SPU_ATTR_NUMBERS_MUST_BE_EQUALS.getCode());
+ }
+ }
+ // 3. 最后校验,每个 Sku 之间不是重复的
+ Set> skuAttrValues = new HashSet<>(); // 每个元素,都是一个 Sku 的 attrValueId 集合。这样,通过最外层的 Set ,判断是否有重复的.
+ for (ProductSkuAddOrUpdateDTO sku : productSkuAddDTOs) {
+ if (!skuAttrValues.add(new HashSet<>(sku.getAttrs()))) { // 添加失败,说明重复
+ throw ServiceExceptionUtil.exception(ProductErrorCodeEnum.PRODUCT_SPU_SKU__NOT_DUPLICATE.getCode());
+ }
+ }
+ }
}
diff --git a/product/product-biz/src/main/resources/mapper/ProductSkuMapper.xml b/product/product-biz/src/main/resources/mapper/ProductSkuMapper.xml
new file mode 100644
index 000000000..8ce298bcd
--- /dev/null
+++ b/product/product-biz/src/main/resources/mapper/ProductSkuMapper.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+ INSERT INTO product_sku (
+ spu_id, status, pic_url, attrs, price,
+ quantity, deleted, create_time
+ ) VALUES
+
+ (#{productSkuDO.spuId}, #{productSkuDO.status}, #{productSkuDO.picUrl}, #{productSkuDO.attrs}, #{productSkuDO.price},
+ #{productSkuDO.quantity}, #{productSkuDO.deleted}, #{productSkuDO.createTime}
+ )
+
+
+
+
+
diff --git a/product/product-biz/src/main/resources/mapper/ProductSpuMapper.xml b/product/product-biz/src/main/resources/mapper/ProductSpuMapper.xml
index 7289e7d6d..c6f19b77c 100644
--- a/product/product-biz/src/main/resources/mapper/ProductSpuMapper.xml
+++ b/product/product-biz/src/main/resources/mapper/ProductSpuMapper.xml
@@ -1,6 +1,6 @@
-
+
diff --git a/product/product-mq/pom.xml b/product/product-mq/pom.xml
new file mode 100644
index 000000000..857d7b9c4
--- /dev/null
+++ b/product/product-mq/pom.xml
@@ -0,0 +1,15 @@
+
+
+
+ demo
+ cn.iocoder.mall
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ product-mq
+
+
+
diff --git a/product/product-mq/src/main/java/cn/iocoder/mall/demo/mq/package-info.java b/product/product-mq/src/main/java/cn/iocoder/mall/demo/mq/package-info.java
new file mode 100644
index 000000000..8172d958c
--- /dev/null
+++ b/product/product-mq/src/main/java/cn/iocoder/mall/demo/mq/package-info.java
@@ -0,0 +1 @@
+package cn.iocoder.mall.demo.mq;
diff --git a/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/dao/user/UserMapper.java b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/dao/user/UserMapper.java
index 90e571d45..1968f0e7a 100644
--- a/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/dao/user/UserMapper.java
+++ b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/dao/user/UserMapper.java
@@ -2,7 +2,6 @@ package cn.iocoder.mall.system.biz.dao.user;
import cn.iocoder.mall.system.biz.dataobject.user.UserDO;
import cn.iocoder.mall.system.biz.dto.user.UserPageDTO;
-import cn.iocoder.mall.system.biz.dto.user.UserUpdateDTO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
@@ -26,6 +25,7 @@ public interface UserMapper extends BaseMapper {
* @return
*/
default IPage selectUserPage(UserPageDTO userPageDTO) {
+ // TODO FROM 芋艿 to jwf1173:看下 QueryWrapperX 噢,已经提供判空啦
return this.selectPage(new Page<>(userPageDTO.getPageNo(), userPageDTO.getPageSize()),
Wrappers.query().lambda()
.eq(StringUtils.isNotBlank(userPageDTO.getNickname()), UserDO::getNickname, userPageDTO.getNickname())
diff --git a/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/dataobject/authorization/RoleDO.java b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/dataobject/authorization/RoleDO.java
index 46354c2de..f6b8611a0 100644
--- a/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/dataobject/authorization/RoleDO.java
+++ b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/dataobject/authorization/RoleDO.java
@@ -1,6 +1,8 @@
package cn.iocoder.mall.system.biz.dataobject.authorization;
import cn.iocoder.mall.mybatis.dataobject.DeletableDO;
+import cn.iocoder.mall.system.biz.enums.authorization.RoleCodeEnum;
+import cn.iocoder.mall.system.biz.enums.authorization.RoleTypeEnum;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -25,10 +27,14 @@ public class RoleDO extends DeletableDO {
private String name;
/**
* 角色编码
+ *
+ * 关联 {@link RoleCodeEnum}
*/
private String code;
/**
* 角色类型
+ *
+ * 关联 {@link RoleTypeEnum}
*/
private Integer type;