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;