sku add
This commit is contained in:
parent
4580f61489
commit
211164d4d1
@ -20,6 +20,7 @@
|
|||||||
<module>product-rest</module>
|
<module>product-rest</module>
|
||||||
<module>product-biz</module>
|
<module>product-biz</module>
|
||||||
<module>product-biz-api</module>
|
<module>product-biz-api</module>
|
||||||
|
<module>product-mq</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
|
@ -1,10 +1,19 @@
|
|||||||
package cn.iocoder.mall.product.biz.enums.category;
|
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;
|
||||||
|
|
||||||
}
|
}
|
@ -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.common.framework.util.StringUtil;
|
||||||
import cn.iocoder.mall.product.biz.bo.product.*;
|
import cn.iocoder.mall.product.biz.bo.product.*;
|
||||||
import cn.iocoder.mall.product.biz.dataobject.category.ProductCategoryDO;
|
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.ProductSkuDO;
|
||||||
import cn.iocoder.mall.product.biz.dataobject.spu.ProductSpuDO;
|
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.sku.ProductSkuAddOrUpdateDTO;
|
||||||
import cn.iocoder.mall.product.biz.dto.product.ProductSpuAddDTO;
|
import cn.iocoder.mall.product.biz.dto.sku.ProductSpuAddDTO;
|
||||||
import cn.iocoder.mall.product.biz.dto.product.ProductSpuUpdateDTO;
|
import cn.iocoder.mall.product.biz.dto.sku.ProductSpuUpdateDTO;
|
||||||
import org.mapstruct.Mapper;
|
import org.mapstruct.Mapper;
|
||||||
import org.mapstruct.Mapping;
|
import org.mapstruct.Mapping;
|
||||||
import org.mapstruct.Mappings;
|
import org.mapstruct.Mappings;
|
||||||
@ -24,6 +24,17 @@ public interface ProductSpuConvert {
|
|||||||
|
|
||||||
ProductSpuConvert INSTANCE = Mappers.getMapper(ProductSpuConvert.class);
|
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({
|
@Mappings({
|
||||||
@Mapping(source = "picUrls", target = "picUrls", qualifiedByName = "translatePicUrlsFromString")
|
@Mapping(source = "picUrls", target = "picUrls", qualifiedByName = "translatePicUrlsFromString")
|
||||||
})
|
})
|
||||||
@ -37,16 +48,6 @@ public interface ProductSpuConvert {
|
|||||||
@Mappings({})
|
@Mappings({})
|
||||||
List<ProductSpuBO> convert(List<ProductSpuDO> spus);
|
List<ProductSpuBO> convert(List<ProductSpuDO> 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({
|
@Mappings({
|
||||||
@Mapping(source = "picUrls", target = "picUrls", ignore = true)
|
@Mapping(source = "picUrls", target = "picUrls", ignore = true)
|
@ -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 cn.iocoder.mall.product.biz.dataobject.spu.ProductSkuDO;
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -16,4 +17,6 @@ public interface ProductSkuMapper extends BaseMapper<ProductSkuDO> {
|
|||||||
.eq(ProductSkuDO::getStatus, status)
|
.eq(ProductSkuDO::getStatus, status)
|
||||||
.eq(ProductSkuDO::getDeleted, false));
|
.eq(ProductSkuDO::getDeleted, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void insertList(@Param("productSkuDOs") List<ProductSkuDO> productSkuDOs);
|
||||||
}
|
}
|
@ -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 cn.iocoder.mall.product.biz.dataobject.spu.ProductSpuDO;
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
@ -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 cn.iocoder.mall.product.biz.dataobject.spu.UserProductSpuCollectionsDO;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
@ -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<Integer> attrs;
|
||||||
|
/**
|
||||||
|
* 价格,单位:分
|
||||||
|
*/
|
||||||
|
@NotNull(message = "价格不能为空")
|
||||||
|
@Min(value = 1L, message = "最小价格为 1")
|
||||||
|
private Integer price;
|
||||||
|
/**
|
||||||
|
* 库存数量
|
||||||
|
*/
|
||||||
|
@NotNull(message = "库存数量不能为空")
|
||||||
|
@Min(value = 1L, message = "最小库存为 1")
|
||||||
|
private Integer quantity;
|
||||||
|
|
||||||
|
}
|
@ -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<String> picUrls;
|
||||||
|
|
||||||
|
// ========== 其他信息 =========
|
||||||
|
/**
|
||||||
|
* 是否上架商品(是否可见)。
|
||||||
|
*
|
||||||
|
* true 为已上架
|
||||||
|
* false 为已下架
|
||||||
|
*/
|
||||||
|
@NotNull(message = "是否上架不能为空")
|
||||||
|
private Boolean visible;
|
||||||
|
|
||||||
|
// ========== SKU =========
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SKU 数组
|
||||||
|
*/
|
||||||
|
@NotNull(message = "SKU 不能为空")
|
||||||
|
private List<ProductSkuAddOrUpdateDTO> skus;
|
||||||
|
|
||||||
|
}
|
@ -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<String> picUrls;
|
||||||
|
|
||||||
|
// ========== 其他信息 =========
|
||||||
|
/**
|
||||||
|
* 是否上架商品(是否可见)。
|
||||||
|
*
|
||||||
|
* true 为已上架
|
||||||
|
* false 为已下架
|
||||||
|
*/
|
||||||
|
@NotNull(message = "是否上架不能为空")
|
||||||
|
private Boolean visible;
|
||||||
|
|
||||||
|
// ========== SKU =========
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SKU 数组
|
||||||
|
*/
|
||||||
|
@NotNull(message = "SKU 不能为空")
|
||||||
|
private List<ProductSkuAddOrUpdateDTO> skus;
|
||||||
|
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package cn.iocoder.mall.product.biz.service.category;
|
package cn.iocoder.mall.product.biz.service.category;
|
||||||
|
|
||||||
import cn.iocoder.mall.product.biz.bo.category.ProductCategoryBO;
|
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.ProductCategoryAddDTO;
|
||||||
import cn.iocoder.mall.product.biz.dto.category.ProductCategoryDeleteDTO;
|
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.ProductCategoryUpdateDTO;
|
||||||
@ -21,12 +22,14 @@ public interface ProductCategoryService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取所有商品分类
|
* 获取所有商品分类
|
||||||
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
List<ProductCategoryBO> getAllProductCategory();
|
List<ProductCategoryBO> getAllProductCategory();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新增商品分类
|
* 新增商品分类
|
||||||
|
*
|
||||||
* @param productCategoryAddDTO
|
* @param productCategoryAddDTO
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@ -34,6 +37,7 @@ public interface ProductCategoryService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新商品分类
|
* 更新商品分类
|
||||||
|
*
|
||||||
* @param productCategoryUpdateDTO
|
* @param productCategoryUpdateDTO
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@ -41,6 +45,7 @@ public interface ProductCategoryService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新商品分类状态
|
* 更新商品分类状态
|
||||||
|
*
|
||||||
* @param productCategoryUpdateStatusDTO
|
* @param productCategoryUpdateStatusDTO
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@ -48,8 +53,17 @@ public interface ProductCategoryService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除商品分类
|
* 删除商品分类
|
||||||
|
*
|
||||||
* @param productCategoryDeleteDTO
|
* @param productCategoryDeleteDTO
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
Boolean deleteProductCategory(@Valid ProductCategoryDeleteDTO productCategoryDeleteDTO);
|
Boolean deleteProductCategory(@Valid ProductCategoryDeleteDTO productCategoryDeleteDTO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验分类是否可用
|
||||||
|
*
|
||||||
|
* @param productCategoryId 分类ID
|
||||||
|
* @return 商品分类
|
||||||
|
*/
|
||||||
|
ProductCategoryDO validProductCategory(Integer productCategoryId);
|
||||||
}
|
}
|
||||||
|
@ -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.ProductCategoryDeleteDTO;
|
||||||
import cn.iocoder.mall.product.biz.dto.category.ProductCategoryUpdateDTO;
|
import cn.iocoder.mall.product.biz.dto.category.ProductCategoryUpdateDTO;
|
||||||
import cn.iocoder.mall.product.biz.dto.category.ProductCategoryUpdateStatusDTO;
|
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.ProductCategoryConstants;
|
||||||
import cn.iocoder.mall.product.biz.enums.category.ProductCategoryStatusEnum;
|
import cn.iocoder.mall.product.biz.enums.category.ProductCategoryStatusEnum;
|
||||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -32,21 +34,12 @@ public class ProductCategoryServiceImpl implements ProductCategoryService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private ProductCategoryMapper productCategoryMapper;
|
private ProductCategoryMapper productCategoryMapper;
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取所有商品分类
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public List<ProductCategoryBO> getAllProductCategory() {
|
public List<ProductCategoryBO> getAllProductCategory() {
|
||||||
List<ProductCategoryDO> categoryList = productCategoryMapper.selectList(null);
|
List<ProductCategoryDO> categoryList = productCategoryMapper.selectList(null);
|
||||||
return ProductCategoryConvert.INSTANCE.convertToAllListBO(categoryList);
|
return ProductCategoryConvert.INSTANCE.convertToAllListBO(categoryList);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 新增商品分类
|
|
||||||
* @param productCategoryAddDTO
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public ProductCategoryBO addProductCategory(ProductCategoryAddDTO productCategoryAddDTO) {
|
public ProductCategoryBO addProductCategory(ProductCategoryAddDTO productCategoryAddDTO) {
|
||||||
// 校验父分类
|
// 校验父分类
|
||||||
@ -62,11 +55,6 @@ public class ProductCategoryServiceImpl implements ProductCategoryService {
|
|||||||
return ProductCategoryConvert.INSTANCE.convertToBO(productCategory);
|
return ProductCategoryConvert.INSTANCE.convertToBO(productCategory);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新商品分类
|
|
||||||
* @param productCategoryUpdateDTO
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Boolean updateProductCategory(ProductCategoryUpdateDTO productCategoryUpdateDTO) {
|
public Boolean updateProductCategory(ProductCategoryUpdateDTO productCategoryUpdateDTO) {
|
||||||
// 校验当前分类是否存在
|
// 校验当前分类是否存在
|
||||||
@ -91,11 +79,6 @@ public class ProductCategoryServiceImpl implements ProductCategoryService {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新商品分类状态
|
|
||||||
* @param productCategoryUpdateStatusDTO
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Boolean updateProductCategoryStatus(ProductCategoryUpdateStatusDTO productCategoryUpdateStatusDTO) {
|
public Boolean updateProductCategoryStatus(ProductCategoryUpdateStatusDTO productCategoryUpdateStatusDTO) {
|
||||||
// 校验商品分类是否存在
|
// 校验商品分类是否存在
|
||||||
@ -118,11 +101,6 @@ public class ProductCategoryServiceImpl implements ProductCategoryService {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除商品分类
|
|
||||||
* @param productCategoryDeleteDTO
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Boolean deleteProductCategory(ProductCategoryDeleteDTO productCategoryDeleteDTO) {
|
public Boolean deleteProductCategory(ProductCategoryDeleteDTO productCategoryDeleteDTO) {
|
||||||
Integer productCategoryId = productCategoryDeleteDTO.getId();
|
Integer productCategoryId = productCategoryDeleteDTO.getId();
|
||||||
@ -149,10 +127,6 @@ public class ProductCategoryServiceImpl implements ProductCategoryService {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 校验商品分类的父分类
|
|
||||||
* @param pid
|
|
||||||
*/
|
|
||||||
private void validParent(Integer pid) {
|
private void validParent(Integer pid) {
|
||||||
if (!ProductCategoryConstants.PID_ROOT.equals(pid)) {
|
if (!ProductCategoryConstants.PID_ROOT.equals(pid)) {
|
||||||
ProductCategoryDO parentCategory = productCategoryMapper.selectById(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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.ServiceExceptionUtil;
|
||||||
import cn.iocoder.common.framework.util.StringUtil;
|
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.ProductAttrAndValuePairBO;
|
||||||
import cn.iocoder.mall.product.biz.bo.product.ProductSpuDetailBO;
|
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.category.ProductCategoryMapper;
|
||||||
import cn.iocoder.mall.product.biz.dao.spu.ProductSkuMapper;
|
import cn.iocoder.mall.product.biz.dao.sku.ProductSkuMapper;
|
||||||
import cn.iocoder.mall.product.biz.dao.spu.ProductSpuMapper;
|
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.category.ProductCategoryDO;
|
||||||
import cn.iocoder.mall.product.biz.dataobject.spu.ProductSkuDO;
|
import cn.iocoder.mall.product.biz.dataobject.spu.ProductSkuDO;
|
||||||
import cn.iocoder.mall.product.biz.dataobject.spu.ProductSpuDO;
|
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.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.enums.spu.ProductSpuConstants;
|
||||||
import cn.iocoder.mall.product.biz.service.attr.ProductAttrService;
|
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.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.*;
|
||||||
import java.util.List;
|
import java.util.stream.Collectors;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class ProductSpuServiceImpl implements ProductSpuService {
|
public class ProductSpuServiceImpl implements ProductSpuService {
|
||||||
@ -33,6 +38,8 @@ public class ProductSpuServiceImpl implements ProductSpuService {
|
|||||||
private ProductCategoryMapper productCategoryMapper;
|
private ProductCategoryMapper productCategoryMapper;
|
||||||
@Autowired
|
@Autowired
|
||||||
private ProductAttrService productAttrService;
|
private ProductAttrService productAttrService;
|
||||||
|
@Autowired
|
||||||
|
private ProductCategoryService productCategoryService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ProductSpuDetailBO getProductSpuDetail(Integer spuId) {
|
public ProductSpuDetailBO getProductSpuDetail(Integer spuId) {
|
||||||
@ -55,4 +62,109 @@ public class ProductSpuServiceImpl implements ProductSpuService {
|
|||||||
return ProductSpuConvert.INSTANCE.convert2(spu, skus, attrAndValuePairList, category);
|
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<Integer> productAttrValueIds = new HashSet<>();
|
||||||
|
productSpuAddDTO.getSkus().forEach(productSkuAddDTO -> productAttrValueIds.addAll(productSkuAddDTO.getAttrs()));
|
||||||
|
// 读取规格时,需要考虑规格是否被禁用
|
||||||
|
List<ProductAttrAndValuePairBO> 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<ProductSkuDO> 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<ProductSkuAddOrUpdateDTO> 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<ProductUpdateMessage> springMessage = MessageBuilder.withPayload(message)
|
||||||
|
// .build();
|
||||||
|
// // 发送消息
|
||||||
|
// return mqStreamProducer.productUpdateOutput().send(springMessage);
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验 sku 是否合法
|
||||||
|
*
|
||||||
|
* @param productSkuAddDTOs sku 添加或修改信息
|
||||||
|
* @param productAttrDetailBOs 商品规格明细数组
|
||||||
|
*/
|
||||||
|
private void validProductSku(List<ProductSkuAddOrUpdateDTO> productSkuAddDTOs, List<ProductAttrAndValuePairBO> productAttrDetailBOs) {
|
||||||
|
// 创建 ProductAttrDetailBO 的映射。其中,KEY 为 ProductAttrDetailBO.attrValueId ,即规格值的编号
|
||||||
|
Map<Integer, ProductAttrAndValuePairBO> productAttrDetailBOMap = productAttrDetailBOs.stream().collect(
|
||||||
|
Collectors.toMap(ProductAttrAndValuePairBO::getAttrValueId, productAttrDetailBO -> productAttrDetailBO));
|
||||||
|
// 1. 先校验,一个 Sku 下,没有重复的规格。校验方式是,遍历每个 Sku ,看看是否有重复的规格 attrId
|
||||||
|
for (ProductSkuAddOrUpdateDTO sku : productSkuAddDTOs) {
|
||||||
|
Set<Integer> 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<Set<Integer>> 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="cn.iocoder.mall.product.biz.dao.sku.ProductSkuMapper">
|
||||||
|
|
||||||
|
|
||||||
|
<insert id="insertList" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
|
||||||
|
INSERT INTO product_sku (
|
||||||
|
spu_id, status, pic_url, attrs, price,
|
||||||
|
quantity, deleted, create_time
|
||||||
|
) VALUES
|
||||||
|
<foreach collection="productSkuDOs" item="productSkuDO" separator=",">
|
||||||
|
(#{productSkuDO.spuId}, #{productSkuDO.status}, #{productSkuDO.picUrl}, #{productSkuDO.attrs}, #{productSkuDO.price},
|
||||||
|
#{productSkuDO.quantity}, #{productSkuDO.deleted}, #{productSkuDO.createTime}
|
||||||
|
)
|
||||||
|
</foreach>
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
|
||||||
|
</mapper>
|
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
<mapper namespace="cn.iocoder.mall.product.biz.dao.spu.ProductSpuMapper">
|
<mapper namespace="cn.iocoder.mall.product.biz.dao.sku.ProductSpuMapper">
|
||||||
|
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
15
product/product-mq/pom.xml
Normal file
15
product/product-mq/pom.xml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>demo</artifactId>
|
||||||
|
<groupId>cn.iocoder.mall</groupId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>product-mq</artifactId>
|
||||||
|
|
||||||
|
|
||||||
|
</project>
|
@ -0,0 +1 @@
|
|||||||
|
package cn.iocoder.mall.demo.mq;
|
Loading…
Reference in New Issue
Block a user