进行商品添加的迁移
parent
be4b34c884
commit
e107b42f53
|
@ -3,5 +3,21 @@ GET {{baseUrl}}/product-spu/page?pageNo=1&pageSize=10
|
||||||
Content-Type: application/x-www-form-urlencoded
|
Content-Type: application/x-www-form-urlencoded
|
||||||
Authorization: Bearer {{accessToken}}
|
Authorization: Bearer {{accessToken}}
|
||||||
|
|
||||||
###
|
### /product-spu/page 成功(有库存 + 上架)
|
||||||
|
GET {{baseUrl}}/product-spu/page?pageNo=1&pageSize=10&hasQuantity=true&visible=true
|
||||||
|
Content-Type: application/x-www-form-urlencoded
|
||||||
|
Authorization: Bearer {{accessToken}}
|
||||||
|
|
||||||
|
### /product-spu/page 成功(无库存 + 下架)
|
||||||
|
GET {{baseUrl}}/product-spu/page?pageNo=1&pageSize=10&hasQuantity=false&visible=false
|
||||||
|
Content-Type: application/x-www-form-urlencoded
|
||||||
|
Authorization: Bearer {{accessToken}}
|
||||||
|
|
||||||
|
### /product-spu/create 成功
|
||||||
|
POST {{baseUrl}}/product-spu/create
|
||||||
|
Content-Type: application/x-www-form-urlencoded
|
||||||
|
Authorization: Bearer {{accessToken}}
|
||||||
|
|
||||||
|
name=测试商品标题&description=测试商品描述&cid=637&sellPoint=丑&picUrls=1,2,3&visible=true&skus[0].price=1&skus[0].quantity=100&skus[0].attrValueIds=1,3
|
||||||
|
|
||||||
|
###
|
||||||
|
|
|
@ -61,6 +61,9 @@ public class ProductSpuController {
|
||||||
@GetMapping("/page")
|
@GetMapping("/page")
|
||||||
@ApiOperation("获得商品 SPU 分页")
|
@ApiOperation("获得商品 SPU 分页")
|
||||||
public CommonResult<PageResult<ProductSpuRespVO>> pageProductSpu(ProductSpuPageReqVO pageVO) {
|
public CommonResult<PageResult<ProductSpuRespVO>> pageProductSpu(ProductSpuPageReqVO pageVO) {
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
return success(productSpuManager.pageProductSpu(pageVO));
|
return success(productSpuManager.pageProductSpu(pageVO));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,15 +3,45 @@ package cn.iocoder.mall.managementweb.controller.product.vo.spu;
|
||||||
import io.swagger.annotations.ApiModel;
|
import io.swagger.annotations.ApiModel;
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import javax.validation.constraints.Min;
|
||||||
import javax.validation.constraints.NotEmpty;
|
import javax.validation.constraints.NotEmpty;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ApiModel("商品 SPU创建 Request VO")
|
@ApiModel("商品 SPU 创建 Request VO")
|
||||||
@Data
|
@Data
|
||||||
public class ProductSpuCreateReqVO {
|
public class ProductSpuCreateReqVO {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SKU 信息
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public static class Sku {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 规格值数组
|
||||||
|
*/
|
||||||
|
@NotNull(message = "规格值数组不能为空")
|
||||||
|
private List<Integer> attrValueIds;
|
||||||
|
/**
|
||||||
|
* 价格,单位:分
|
||||||
|
*/
|
||||||
|
@NotNull(message = "价格不能为空")
|
||||||
|
@Min(value = 1L, message = "最小价格为 1")
|
||||||
|
private Integer price;
|
||||||
|
/**
|
||||||
|
* 库存数量
|
||||||
|
*/
|
||||||
|
@NotNull(message = "库存数量不能为空")
|
||||||
|
@Min(value = 1L, message = "最小库存为 1")
|
||||||
|
private Integer quantity;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@ApiModelProperty(value = "SPU 名字", required = true)
|
@ApiModelProperty(value = "SPU 名字", required = true)
|
||||||
@NotEmpty(message = "SPU 名字不能为空")
|
@NotEmpty(message = "SPU 名字不能为空")
|
||||||
private String name;
|
private String name;
|
||||||
|
@ -30,14 +60,14 @@ public class ProductSpuCreateReqVO {
|
||||||
@ApiModelProperty(value = "是否上架商品", required = true)
|
@ApiModelProperty(value = "是否上架商品", required = true)
|
||||||
@NotNull(message = "是否上架商品不能为空")
|
@NotNull(message = "是否上架商品不能为空")
|
||||||
private Boolean visible;
|
private Boolean visible;
|
||||||
@ApiModelProperty(value = "排序字段", required = true)
|
|
||||||
@NotNull(message = "排序字段不能为空")
|
// ========== SKU =========
|
||||||
private Integer sort;
|
|
||||||
@ApiModelProperty(value = "价格", required = true)
|
/**
|
||||||
@NotNull(message = "价格不能为空")
|
* SKU 数组
|
||||||
private Integer price;
|
*/
|
||||||
@ApiModelProperty(value = "库存数量", required = true)
|
@NotNull(message = "SKU 不能为空")
|
||||||
@NotNull(message = "库存数量不能为空")
|
@Valid
|
||||||
private Integer quantity;
|
private List<Sku> skus;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,23 +11,13 @@ import lombok.EqualsAndHashCode;
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
public class ProductSpuPageReqVO extends PageParam {
|
public class ProductSpuPageReqVO extends PageParam {
|
||||||
|
|
||||||
@ApiModelProperty(value = "SPU 名字", required = true)
|
@ApiModelProperty(value = "SPU 名字", notes = "模糊匹配", example = "艿艿")
|
||||||
private String name;
|
private String name;
|
||||||
@ApiModelProperty(value = "卖点", required = true)
|
@ApiModelProperty(value = "分类编号", example = "1024")
|
||||||
private String sellPoint;
|
|
||||||
@ApiModelProperty(value = "描述", required = true)
|
|
||||||
private String description;
|
|
||||||
@ApiModelProperty(value = "分类编号", required = true)
|
|
||||||
private Integer cid;
|
private Integer cid;
|
||||||
@ApiModelProperty(value = "商品主图地址", required = true)
|
@ApiModelProperty(value = "是否上架商品", example = "true")
|
||||||
private String picUrls;
|
private Boolean visible;
|
||||||
@ApiModelProperty(value = "是否上架商品", required = true)
|
@ApiModelProperty(value = "是否有库存", example = "true")
|
||||||
private Integer visible;
|
private Boolean hasQuantity;
|
||||||
@ApiModelProperty(value = "排序字段", required = true)
|
|
||||||
private Integer sort;
|
|
||||||
@ApiModelProperty(value = "价格", required = true)
|
|
||||||
private Integer price;
|
|
||||||
@ApiModelProperty(value = "库存数量", required = true)
|
|
||||||
private Integer quantity;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import cn.iocoder.mall.managementweb.controller.product.vo.spu.ProductSpuCreateR
|
||||||
import cn.iocoder.mall.managementweb.controller.product.vo.spu.ProductSpuPageReqVO;
|
import cn.iocoder.mall.managementweb.controller.product.vo.spu.ProductSpuPageReqVO;
|
||||||
import cn.iocoder.mall.managementweb.controller.product.vo.spu.ProductSpuRespVO;
|
import cn.iocoder.mall.managementweb.controller.product.vo.spu.ProductSpuRespVO;
|
||||||
import cn.iocoder.mall.managementweb.controller.product.vo.spu.ProductSpuUpdateReqVO;
|
import cn.iocoder.mall.managementweb.controller.product.vo.spu.ProductSpuUpdateReqVO;
|
||||||
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuCreateReqDTO;
|
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuAndSkuCreateReqDTO;
|
||||||
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuPageReqDTO;
|
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuPageReqDTO;
|
||||||
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuRespDTO;
|
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuRespDTO;
|
||||||
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuUpdateReqDTO;
|
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuUpdateReqDTO;
|
||||||
|
@ -19,7 +19,7 @@ public interface ProductSpuConvert {
|
||||||
|
|
||||||
ProductSpuConvert INSTANCE = Mappers.getMapper(ProductSpuConvert.class);
|
ProductSpuConvert INSTANCE = Mappers.getMapper(ProductSpuConvert.class);
|
||||||
|
|
||||||
ProductSpuCreateReqDTO convert(ProductSpuCreateReqVO bean);
|
ProductSpuAndSkuCreateReqDTO convert(ProductSpuCreateReqVO bean);
|
||||||
|
|
||||||
ProductSpuUpdateReqDTO convert(ProductSpuUpdateReqVO bean);
|
ProductSpuUpdateReqDTO convert(ProductSpuUpdateReqVO bean);
|
||||||
|
|
||||||
|
|
|
@ -18,15 +18,15 @@ public interface ProductErrorCodeConstants {
|
||||||
ErrorCode PRODUCT_CATEGORY_PARENT_CAN_NOT_BE_LEVEL2 = new ErrorCode(1002001005, "父分类必须是一级分类");
|
ErrorCode PRODUCT_CATEGORY_PARENT_CAN_NOT_BE_LEVEL2 = new ErrorCode(1002001005, "父分类必须是一级分类");
|
||||||
|
|
||||||
// ========== PRODUCT SPU + SKU 模块 ==========
|
// ========== PRODUCT SPU + SKU 模块 ==========
|
||||||
ErrorCode PRODUCT_SKU_ATTR_CANT_NOT_DUPLICATE = new ErrorCode(1003002000, "一个 Sku 下,不能有重复的规格");
|
ErrorCode PRODUCT_SKU_ATTR_CANT_NOT_DUPLICATE = new ErrorCode(1003002000, "一个 SKU 下,不能有重复的规格");
|
||||||
ErrorCode PRODUCT_SPU_ATTR_NUMBERS_MUST_BE_EQUALS = new ErrorCode(1003002001, "一个 Spu 下的每个 Sku ,其规格数必须一致");
|
ErrorCode PRODUCT_SPU_ATTR_NUMBERS_MUST_BE_EQUALS = new ErrorCode(1003002001, "一个 Spu 下的每个 SKU ,其规格数必须一致");
|
||||||
ErrorCode PRODUCT_SPU_SKU__NOT_DUPLICATE = new ErrorCode(1003002002, "一个 Spu 下的每个 Sku ,必须不重复");
|
ErrorCode PRODUCT_SPU_SKU_NOT_DUPLICATE = new ErrorCode(1003002002, "一个 SPU 下的每个 SKU ,必须不重复");
|
||||||
ErrorCode PRODUCT_SPU_NOT_EXISTS = new ErrorCode(1003002003, "Spu 不存在");
|
ErrorCode PRODUCT_SPU_NOT_EXISTS = new ErrorCode(1003002003, "SPU 不存在");
|
||||||
ErrorCode PRODUCT_SPU_CATEGORY_MUST_BE_LEVEL2 = new ErrorCode(1003002003, "Spu 只能添加在二级分类下");
|
ErrorCode PRODUCT_SPU_CATEGORY_MUST_BE_LEVEL2 = new ErrorCode(1003002003, "SPU 只能添加在二级分类下");
|
||||||
|
|
||||||
// ========== PRODUCT ATTR + ATTR_VALUE 模块 ==========
|
// ========== PRODUCT ATTR + ATTR_VALUE 模块 ==========
|
||||||
ErrorCode PRODUCT_ATTR_VALUE_NOT_EXIST = new ErrorCode(1003003000, "商品属性值不存在");
|
ErrorCode PRODUCT_ATTR_VALUE_NOT_EXIST = new ErrorCode(1003003000, "商品属性值不存在");
|
||||||
ErrorCode PRODUCT_ATTR_NOT_EXIST = new ErrorCode(1003003001, "商品属性值不存在");
|
ErrorCode PRODUCT_ATTR_KEY_NOT_EXIST = new ErrorCode(1003003001, "商品属性值不存在");
|
||||||
ErrorCode PRODUCT_ATTR_EXISTS = new ErrorCode(1003003002, "商品规格已经存在");
|
ErrorCode PRODUCT_ATTR_EXISTS = new ErrorCode(1003003002, "商品规格已经存在");
|
||||||
ErrorCode PRODUCT_ATTR_STATUS_EQUALS = new ErrorCode(1003003003, "商品规格已经是该状态");
|
ErrorCode PRODUCT_ATTR_STATUS_EQUALS = new ErrorCode(1003003003, "商品规格已经是该状态");
|
||||||
ErrorCode PRODUCT_ATTR_VALUE_EXISTS = new ErrorCode(1003003004, "商品规格值已经存在");
|
ErrorCode PRODUCT_ATTR_VALUE_EXISTS = new ErrorCode(1003003004, "商品规格值已经存在");
|
||||||
|
|
|
@ -2,7 +2,7 @@ package cn.iocoder.mall.productservice.rpc.spu;
|
||||||
|
|
||||||
import cn.iocoder.common.framework.vo.CommonResult;
|
import cn.iocoder.common.framework.vo.CommonResult;
|
||||||
import cn.iocoder.common.framework.vo.PageResult;
|
import cn.iocoder.common.framework.vo.PageResult;
|
||||||
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuCreateReqDTO;
|
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuAndSkuCreateReqDTO;
|
||||||
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuPageReqDTO;
|
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuPageReqDTO;
|
||||||
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuRespDTO;
|
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuRespDTO;
|
||||||
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuUpdateReqDTO;
|
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuUpdateReqDTO;
|
||||||
|
@ -20,7 +20,7 @@ public interface ProductSpuRpc {
|
||||||
* @param createDTO 创建商品 SPU DTO
|
* @param createDTO 创建商品 SPU DTO
|
||||||
* @return 商品 SPU编号
|
* @return 商品 SPU编号
|
||||||
*/
|
*/
|
||||||
CommonResult<Integer> createProductSpu(ProductSpuCreateReqDTO createDTO);
|
CommonResult<Integer> createProductSpu(ProductSpuAndSkuCreateReqDTO createDTO);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新商品 SPU
|
* 更新商品 SPU
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
package cn.iocoder.mall.productservice.rpc.spu.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import javax.validation.constraints.Min;
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商品 SPU 和 SKU 创建 Request DTO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class ProductSpuAndSkuCreateReqDTO implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SKU 信息
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public static class Sku implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 规格值数组
|
||||||
|
*/
|
||||||
|
@NotNull(message = "规格值数组不能为空")
|
||||||
|
private List<Integer> attrValueIds;
|
||||||
|
/**
|
||||||
|
* 价格,单位:分
|
||||||
|
*/
|
||||||
|
@NotNull(message = "价格不能为空")
|
||||||
|
@Min(value = 1L, message = "最小价格为 1")
|
||||||
|
private Integer price;
|
||||||
|
/**
|
||||||
|
* 库存数量
|
||||||
|
*/
|
||||||
|
@NotNull(message = "库存数量不能为空")
|
||||||
|
@Min(value = 1L, message = "最小库存为 1")
|
||||||
|
private Integer quantity;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 基本信息 =========
|
||||||
|
/**
|
||||||
|
* SPU 名字
|
||||||
|
*/
|
||||||
|
@NotEmpty(message = "SPU 名字不能为空")
|
||||||
|
private String name;
|
||||||
|
/**
|
||||||
|
* 卖点
|
||||||
|
*/
|
||||||
|
@NotEmpty(message = "卖点不能为空")
|
||||||
|
private String sellPoint;
|
||||||
|
/**
|
||||||
|
* 描述
|
||||||
|
*/
|
||||||
|
@NotEmpty(message = "描述不能为空")
|
||||||
|
private String description;
|
||||||
|
/**
|
||||||
|
* 分类编号
|
||||||
|
*/
|
||||||
|
@NotNull(message = "分类编号不能为空")
|
||||||
|
private Integer cid;
|
||||||
|
/**
|
||||||
|
* 商品主图地址
|
||||||
|
*/
|
||||||
|
@NotEmpty(message = "商品主图地址不能为空")
|
||||||
|
private List<String> picUrls;
|
||||||
|
|
||||||
|
// ========== 其他信息 =========
|
||||||
|
/**
|
||||||
|
* 是否上架商品
|
||||||
|
*/
|
||||||
|
@NotNull(message = "是否上架商品不能为空")
|
||||||
|
private Boolean visible;
|
||||||
|
|
||||||
|
// ========== SKU =========
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SKU 数组
|
||||||
|
*/
|
||||||
|
@NotNull(message = "SKU 不能为空")
|
||||||
|
@Valid
|
||||||
|
private List<Sku> skus;
|
||||||
|
|
||||||
|
}
|
|
@ -1,64 +0,0 @@
|
||||||
package cn.iocoder.mall.productservice.rpc.spu.dto;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.experimental.Accessors;
|
|
||||||
|
|
||||||
import javax.validation.constraints.NotEmpty;
|
|
||||||
import javax.validation.constraints.NotNull;
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 商品 SPU 创建 Request DTO
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@Accessors(chain = true)
|
|
||||||
public class ProductSpuCreateReqDTO implements Serializable {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SPU 名字
|
|
||||||
*/
|
|
||||||
@NotEmpty(message = "SPU 名字不能为空")
|
|
||||||
private String name;
|
|
||||||
/**
|
|
||||||
* 卖点
|
|
||||||
*/
|
|
||||||
@NotEmpty(message = "卖点不能为空")
|
|
||||||
private String sellPoint;
|
|
||||||
/**
|
|
||||||
* 描述
|
|
||||||
*/
|
|
||||||
@NotEmpty(message = "描述不能为空")
|
|
||||||
private String description;
|
|
||||||
/**
|
|
||||||
* 分类编号
|
|
||||||
*/
|
|
||||||
@NotNull(message = "分类编号不能为空")
|
|
||||||
private Integer cid;
|
|
||||||
/**
|
|
||||||
* 商品主图地址
|
|
||||||
*/
|
|
||||||
@NotEmpty(message = "商品主图地址不能为空")
|
|
||||||
private List<String> picUrls;
|
|
||||||
/**
|
|
||||||
* 是否上架商品
|
|
||||||
*/
|
|
||||||
@NotNull(message = "是否上架商品不能为空")
|
|
||||||
private Boolean visible;
|
|
||||||
/**
|
|
||||||
* 排序字段
|
|
||||||
*/
|
|
||||||
@NotNull(message = "排序字段不能为空")
|
|
||||||
private Integer sort;
|
|
||||||
/**
|
|
||||||
* 价格
|
|
||||||
*/
|
|
||||||
@NotNull(message = "价格不能为空")
|
|
||||||
private Integer price;
|
|
||||||
/**
|
|
||||||
* 库存数量
|
|
||||||
*/
|
|
||||||
@NotNull(message = "库存数量不能为空")
|
|
||||||
private Integer quantity;
|
|
||||||
|
|
||||||
}
|
|
|
@ -17,6 +17,14 @@ public class ProductSpuPageReqDTO extends PageParam {
|
||||||
* SPU 名字
|
* SPU 名字
|
||||||
*/
|
*/
|
||||||
private String name;
|
private String name;
|
||||||
|
/**
|
||||||
|
* 分类编号
|
||||||
|
*/
|
||||||
|
private Integer cid;
|
||||||
|
/**
|
||||||
|
* 是否可见
|
||||||
|
*/
|
||||||
|
private Boolean visible;
|
||||||
/**
|
/**
|
||||||
* 是否有库存
|
* 是否有库存
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
package cn.iocoder.mall.productservice.convert.sku;
|
||||||
|
|
||||||
|
import cn.iocoder.common.framework.util.StringUtils;
|
||||||
|
import cn.iocoder.mall.productservice.dal.mysql.dataobject.spu.ProductSkuDO;
|
||||||
|
import cn.iocoder.mall.productservice.service.sku.bo.ProductSkuCreateOrUpdateBO;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
|
import org.mapstruct.Named;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface ProductSkuConvert {
|
||||||
|
|
||||||
|
ProductSkuConvert INSTANCE = Mappers.getMapper(ProductSkuConvert.class);
|
||||||
|
|
||||||
|
List<ProductSkuDO> convertList(List<ProductSkuCreateOrUpdateBO> list);
|
||||||
|
|
||||||
|
@Mapping(source = "attrValueIds", target = "attrs", qualifiedByName = "translatePicUrlsFromStringList")
|
||||||
|
ProductSkuDO convertList(ProductSkuCreateOrUpdateBO bean);
|
||||||
|
|
||||||
|
@Named("translateAttrValueIdsFromString")
|
||||||
|
default List<String> translateAttrValueIdsFromString(String attrValueIdsStar) {
|
||||||
|
return StringUtils.split(attrValueIdsStar, ",");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Named("translateAttrValueIdsFromList")
|
||||||
|
default String translateAttrValueIdsFromList(List<Integer> attrValueIds) {
|
||||||
|
return StringUtils.join(attrValueIds, ",");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -3,7 +3,8 @@ package cn.iocoder.mall.productservice.convert.spu;
|
||||||
import cn.iocoder.common.framework.util.StringUtils;
|
import cn.iocoder.common.framework.util.StringUtils;
|
||||||
import cn.iocoder.common.framework.vo.PageResult;
|
import cn.iocoder.common.framework.vo.PageResult;
|
||||||
import cn.iocoder.mall.productservice.dal.mysql.dataobject.spu.ProductSpuDO;
|
import cn.iocoder.mall.productservice.dal.mysql.dataobject.spu.ProductSpuDO;
|
||||||
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuCreateReqDTO;
|
import cn.iocoder.mall.productservice.service.sku.bo.ProductSkuCreateOrUpdateBO;
|
||||||
|
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuAndSkuCreateReqDTO;
|
||||||
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuPageReqDTO;
|
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuPageReqDTO;
|
||||||
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuRespDTO;
|
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuRespDTO;
|
||||||
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuUpdateReqDTO;
|
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuUpdateReqDTO;
|
||||||
|
@ -38,7 +39,7 @@ public interface ProductSpuConvert {
|
||||||
@Mapping(source = "records", target = "list")
|
@Mapping(source = "records", target = "list")
|
||||||
PageResult<ProductSpuBO> convertPage(IPage<ProductSpuDO> page);
|
PageResult<ProductSpuBO> convertPage(IPage<ProductSpuDO> page);
|
||||||
|
|
||||||
ProductSpuCreateBO convert(ProductSpuCreateReqDTO bean);
|
ProductSpuCreateBO convert(ProductSpuAndSkuCreateReqDTO bean);
|
||||||
|
|
||||||
ProductSpuUpdateBO convert(ProductSpuUpdateReqDTO bean);
|
ProductSpuUpdateBO convert(ProductSpuUpdateReqDTO bean);
|
||||||
|
|
||||||
|
@ -51,13 +52,15 @@ public interface ProductSpuConvert {
|
||||||
PageResult<ProductSpuRespDTO> convertPage(PageResult<ProductSpuBO> page);
|
PageResult<ProductSpuRespDTO> convertPage(PageResult<ProductSpuBO> page);
|
||||||
|
|
||||||
@Named("translatePicUrlsFromString")
|
@Named("translatePicUrlsFromString")
|
||||||
default List<String> translatePicUrlsFromString(String picUrls) {
|
default List<String> translatePicUrlsFromList(String picUrls) {
|
||||||
return StringUtils.split(picUrls, ",");
|
return StringUtils.split(picUrls, ",");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Named("translatePicUrlsFromStringList")
|
@Named("translatePicUrlsFromStringList")
|
||||||
default String translatePicUrlsFromString(List<String> picUrls) {
|
default String translatePicUrlsFromList(List<String> picUrls) {
|
||||||
return StringUtils.join(picUrls, ",");
|
return StringUtils.join(picUrls, ",");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<ProductSkuCreateOrUpdateBO> convert(List<ProductSpuAndSkuCreateReqDTO.Sku> list);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
package cn.iocoder.mall.productservice.dal.mysql.dataobject.attr;
|
||||||
|
|
||||||
|
import cn.iocoder.common.framework.enums.CommonStatusEnum;
|
||||||
|
import cn.iocoder.mall.mybatis.core.dataobject.DeletableDO;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Product 规格键 DO
|
||||||
|
*/
|
||||||
|
@TableName("product_attr_key")
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class ProductAttrKeyDO extends DeletableDO {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 规格编号
|
||||||
|
*/
|
||||||
|
private Integer id;
|
||||||
|
/**
|
||||||
|
* 名称
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
/**
|
||||||
|
* 状态
|
||||||
|
*
|
||||||
|
* 枚举 {@link CommonStatusEnum}
|
||||||
|
*/
|
||||||
|
private Integer status;
|
||||||
|
|
||||||
|
}
|
|
@ -1,13 +1,16 @@
|
||||||
package cn.iocoder.mall.product.biz.dataobject.attr;
|
package cn.iocoder.mall.productservice.dal.mysql.dataobject.attr;
|
||||||
|
|
||||||
|
import cn.iocoder.common.framework.enums.CommonStatusEnum;
|
||||||
import cn.iocoder.mall.mybatis.core.dataobject.DeletableDO;
|
import cn.iocoder.mall.mybatis.core.dataobject.DeletableDO;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Product 规格值
|
* 商品规格值 DO
|
||||||
*/
|
*/
|
||||||
|
@TableName("product_attr_value")
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
@Accessors(chain = true)
|
@Accessors(chain = true)
|
||||||
|
@ -27,9 +30,8 @@ public class ProductAttrValueDO extends DeletableDO {
|
||||||
private String name;
|
private String name;
|
||||||
/**
|
/**
|
||||||
* 状态
|
* 状态
|
||||||
* <p>
|
*
|
||||||
* 1-正常
|
* 枚举 {@link CommonStatusEnum}
|
||||||
* 2-禁用
|
|
||||||
*/
|
*/
|
||||||
private Integer status;
|
private Integer status;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package cn.iocoder.mall.productservice.dal.mysql.dataobject.spu;
|
package cn.iocoder.mall.productservice.dal.mysql.dataobject.spu;
|
||||||
|
|
||||||
|
import cn.iocoder.common.framework.enums.CommonStatusEnum;
|
||||||
import cn.iocoder.mall.mybatis.core.dataobject.DeletableDO;
|
import cn.iocoder.mall.mybatis.core.dataobject.DeletableDO;
|
||||||
|
import cn.iocoder.mall.productservice.dal.mysql.dataobject.attr.ProductAttrValueDO;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
@ -29,8 +31,7 @@ public class ProductSkuDO extends DeletableDO {
|
||||||
/**
|
/**
|
||||||
* 状态
|
* 状态
|
||||||
*
|
*
|
||||||
* 1-正常
|
* 枚举 {@link CommonStatusEnum}
|
||||||
* 2-禁用
|
|
||||||
*/
|
*/
|
||||||
private Integer status;
|
private Integer status;
|
||||||
/**
|
/**
|
||||||
|
@ -38,7 +39,7 @@ public class ProductSkuDO extends DeletableDO {
|
||||||
*/
|
*/
|
||||||
private String picUrl;
|
private String picUrl;
|
||||||
/**
|
/**
|
||||||
* 规格值({@link ProductAttrDO})数组
|
* 规格值({@link ProductAttrValueDO#getId()})数组
|
||||||
*
|
*
|
||||||
* 数组,以逗号分隔
|
* 数组,以逗号分隔
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
package cn.iocoder.mall.productservice.dal.mysql.mapper.attr;
|
||||||
|
|
||||||
|
import cn.iocoder.mall.productservice.dal.mysql.dataobject.attr.ProductAttrKeyDO;
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface ProductAttrKeyMapper extends BaseMapper<ProductAttrKeyDO> {
|
||||||
|
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
package cn.iocoder.mall.product.biz.dao.attr;
|
package cn.iocoder.mall.productservice.dal.mysql.mapper.attr;
|
||||||
|
|
||||||
import cn.iocoder.mall.product.biz.dataobject.attr.ProductAttrValueDO;
|
import cn.iocoder.mall.productservice.dal.mysql.dataobject.attr.ProductAttrValueDO;
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
@ -8,4 +8,4 @@ import org.springframework.stereotype.Repository;
|
||||||
public interface ProductAttrValueMapper extends BaseMapper<ProductAttrValueDO> {
|
public interface ProductAttrValueMapper extends BaseMapper<ProductAttrValueDO> {
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package cn.iocoder.mall.productservice.dal.mysql.mapper.sku;
|
||||||
|
|
||||||
|
import cn.iocoder.mall.mybatis.core.query.QueryWrapperX;
|
||||||
|
import cn.iocoder.mall.productservice.dal.mysql.dataobject.spu.ProductSkuDO;
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface ProductSkuMapper extends BaseMapper<ProductSkuDO> {
|
||||||
|
|
||||||
|
default List<ProductSkuDO> selectListBySpuIdAndStatus(Integer spuId, Integer status) {
|
||||||
|
return selectList(new QueryWrapperX<ProductSkuDO>().eq("spu_id", spuId)
|
||||||
|
.eq("status", status));
|
||||||
|
}
|
||||||
|
|
||||||
|
void insertList(@Param("productSkuDOs") List<ProductSkuDO> productSkuDOs);
|
||||||
|
|
||||||
|
}
|
|
@ -12,7 +12,8 @@ import org.springframework.stereotype.Repository;
|
||||||
public interface ProductSpuMapper extends BaseMapper<ProductSpuDO> {
|
public interface ProductSpuMapper extends BaseMapper<ProductSpuDO> {
|
||||||
|
|
||||||
default IPage<ProductSpuDO> selectPage(ProductSpuPageBO pageBO) {
|
default IPage<ProductSpuDO> selectPage(ProductSpuPageBO pageBO) {
|
||||||
QueryWrapperX<ProductSpuDO> query = new QueryWrapperX<ProductSpuDO>().likeIfPresent("name", pageBO.getName());
|
QueryWrapperX<ProductSpuDO> query = new QueryWrapperX<ProductSpuDO>().likeIfPresent("name", pageBO.getName())
|
||||||
|
.eqIfPresent("cid", pageBO.getCid()).eqIfPresent("visible", pageBO.getVisible());
|
||||||
// 库存过滤
|
// 库存过滤
|
||||||
if (pageBO.getHasQuantity() != null) {
|
if (pageBO.getHasQuantity() != null) {
|
||||||
if (pageBO.getHasQuantity()) {
|
if (pageBO.getHasQuantity()) {
|
||||||
|
|
|
@ -1,17 +1,33 @@
|
||||||
package cn.iocoder.mall.productservice.manager.spu;
|
package cn.iocoder.mall.productservice.manager.spu;
|
||||||
|
|
||||||
|
import cn.iocoder.common.framework.exception.util.ServiceExceptionUtil;
|
||||||
import cn.iocoder.common.framework.vo.PageResult;
|
import cn.iocoder.common.framework.vo.PageResult;
|
||||||
import cn.iocoder.mall.productservice.convert.spu.ProductSpuConvert;
|
import cn.iocoder.mall.productservice.convert.spu.ProductSpuConvert;
|
||||||
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuCreateReqDTO;
|
import cn.iocoder.mall.productservice.enums.category.ProductCategoryIdEnum;
|
||||||
|
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuAndSkuCreateReqDTO;
|
||||||
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuPageReqDTO;
|
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuPageReqDTO;
|
||||||
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuRespDTO;
|
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuRespDTO;
|
||||||
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuUpdateReqDTO;
|
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuUpdateReqDTO;
|
||||||
|
import cn.iocoder.mall.productservice.service.attr.ProductAttrService;
|
||||||
|
import cn.iocoder.mall.productservice.service.attr.bo.ProductAttrKeyValueBO;
|
||||||
|
import cn.iocoder.mall.productservice.service.category.ProductCategoryService;
|
||||||
|
import cn.iocoder.mall.productservice.service.category.bo.ProductCategoryBO;
|
||||||
|
import cn.iocoder.mall.productservice.service.sku.ProductSkuService;
|
||||||
|
import cn.iocoder.mall.productservice.service.sku.bo.ProductSkuCreateOrUpdateBO;
|
||||||
import cn.iocoder.mall.productservice.service.spu.ProductSpuService;
|
import cn.iocoder.mall.productservice.service.spu.ProductSpuService;
|
||||||
import cn.iocoder.mall.productservice.service.spu.bo.ProductSpuBO;
|
import cn.iocoder.mall.productservice.service.spu.bo.ProductSpuBO;
|
||||||
|
import cn.iocoder.mall.productservice.service.spu.bo.ProductSpuCreateBO;
|
||||||
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 java.util.Comparator;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static cn.iocoder.mall.productservice.enums.ProductErrorCodeConstants.PRODUCT_CATEGORY_NOT_EXISTS;
|
||||||
|
import static cn.iocoder.mall.productservice.enums.ProductErrorCodeConstants.PRODUCT_SPU_CATEGORY_MUST_BE_LEVEL2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 商品 SPU Manager
|
* 商品 SPU Manager
|
||||||
|
@ -21,16 +37,46 @@ public class ProductSpuManager {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ProductSpuService productSpuService;
|
private ProductSpuService productSpuService;
|
||||||
|
@Autowired
|
||||||
|
private ProductSkuService productSkuService;
|
||||||
|
@Autowired
|
||||||
|
private ProductCategoryService productCategoryService;
|
||||||
|
@Autowired
|
||||||
|
private ProductAttrService productAttrService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建商品 SPU
|
* 创建商品 SPU 和 SKU
|
||||||
*
|
*
|
||||||
* @param createDTO 创建商品 SPU DTO
|
* @param createDTO 创建商品 SPU 和 SKU DTO
|
||||||
* @return 商品 SPU
|
* @return 商品 SPU
|
||||||
*/
|
*/
|
||||||
public Integer createProductSpu(ProductSpuCreateReqDTO createDTO) {
|
@Transactional
|
||||||
ProductSpuBO productSpuBO = productSpuService.createProductSpu(ProductSpuConvert.INSTANCE.convert(createDTO));
|
public Integer createProductSpu(ProductSpuAndSkuCreateReqDTO createDTO) {
|
||||||
return productSpuBO.getId();
|
// 校验商品分类分类存在
|
||||||
|
ProductCategoryBO categoryBO = productCategoryService.getProductCategory(createDTO.getCid());
|
||||||
|
if (categoryBO == null) {
|
||||||
|
// 不存在
|
||||||
|
throw ServiceExceptionUtil.exception(PRODUCT_CATEGORY_NOT_EXISTS);
|
||||||
|
}
|
||||||
|
if (ProductCategoryIdEnum.ROOT.getId().equals(categoryBO.getPid())) {
|
||||||
|
// 商品只能添加到二级分类下
|
||||||
|
throw ServiceExceptionUtil.exception(PRODUCT_SPU_CATEGORY_MUST_BE_LEVEL2);
|
||||||
|
}
|
||||||
|
// 校验规格是否存在
|
||||||
|
Set<Integer> attrValueIds = new HashSet<>();
|
||||||
|
createDTO.getSkus().forEach(productSkuAddDTO -> attrValueIds.addAll(productSkuAddDTO.getAttrValueIds()));
|
||||||
|
List<ProductAttrKeyValueBO> attrKeyValueBOs = productAttrService.validProductAttr(attrValueIds, true);
|
||||||
|
// 创建商品 SKU 对象,并进行校验
|
||||||
|
List<ProductSkuCreateOrUpdateBO> skus = ProductSpuConvert.INSTANCE.convert(createDTO.getSkus());
|
||||||
|
productSkuService.validProductSku(skus, attrKeyValueBOs);
|
||||||
|
// 插入商品 SPU 记录
|
||||||
|
ProductSpuCreateBO spuCreateBO = ProductSpuConvert.INSTANCE.convert(createDTO).setSort(0);
|
||||||
|
spuCreateBO.setPrice(skus.stream().min(Comparator.comparing(ProductSkuCreateOrUpdateBO::getPrice)).get().getPrice()); // 求最小价格
|
||||||
|
spuCreateBO.setQuantity(skus.stream().mapToInt(ProductSkuCreateOrUpdateBO::getQuantity).sum()); // 求库存之和
|
||||||
|
ProductSpuBO spuBO = productSpuService.createProductSpu(spuCreateBO);
|
||||||
|
// 从插入商品 SKU 记录
|
||||||
|
productSkuService.createProductSkus(spuBO.getId(), skus);
|
||||||
|
return spuBO.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -71,7 +117,8 @@ public class ProductSpuManager {
|
||||||
* @return 商品 SPU分页结果
|
* @return 商品 SPU分页结果
|
||||||
*/
|
*/
|
||||||
public PageResult<ProductSpuRespDTO> pageProductSpu(ProductSpuPageReqDTO pageDTO) {
|
public PageResult<ProductSpuRespDTO> pageProductSpu(ProductSpuPageReqDTO pageDTO) {
|
||||||
PageResult<ProductSpuBO> pageResultBO = productSpuService.pageProductSpu(ProductSpuConvert.INSTANCE.convert(pageDTO));
|
PageResult<ProductSpuBO> pageResultBO = productSpuService.pageProductSpu(ProductSpuConvert.INSTANCE.convert(pageDTO));
|
||||||
return ProductSpuConvert.INSTANCE.convertPage(pageResultBO);
|
return ProductSpuConvert.INSTANCE.convertPage(pageResultBO);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ package cn.iocoder.mall.productservice.rpc.spu;
|
||||||
import cn.iocoder.common.framework.vo.CommonResult;
|
import cn.iocoder.common.framework.vo.CommonResult;
|
||||||
import cn.iocoder.common.framework.vo.PageResult;
|
import cn.iocoder.common.framework.vo.PageResult;
|
||||||
import cn.iocoder.mall.productservice.manager.spu.ProductSpuManager;
|
import cn.iocoder.mall.productservice.manager.spu.ProductSpuManager;
|
||||||
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuCreateReqDTO;
|
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuAndSkuCreateReqDTO;
|
||||||
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuPageReqDTO;
|
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuPageReqDTO;
|
||||||
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuRespDTO;
|
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuRespDTO;
|
||||||
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuUpdateReqDTO;
|
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuUpdateReqDTO;
|
||||||
|
@ -24,7 +24,7 @@ public class ProductSpuRpcImpl implements ProductSpuRpc {
|
||||||
private ProductSpuManager productSpuManager;
|
private ProductSpuManager productSpuManager;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommonResult<Integer> createProductSpu(ProductSpuCreateReqDTO createDTO) {
|
public CommonResult<Integer> createProductSpu(ProductSpuAndSkuCreateReqDTO createDTO) {
|
||||||
return success(productSpuManager.createProductSpu(createDTO));
|
return success(productSpuManager.createProductSpu(createDTO));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
package cn.iocoder.mall.productservice.service.attr;
|
||||||
|
|
||||||
|
import cn.iocoder.common.framework.enums.CommonStatusEnum;
|
||||||
|
import cn.iocoder.common.framework.exception.util.ServiceExceptionUtil;
|
||||||
|
import cn.iocoder.common.framework.util.CollectionUtils;
|
||||||
|
import cn.iocoder.mall.productservice.dal.mysql.dataobject.attr.ProductAttrKeyDO;
|
||||||
|
import cn.iocoder.mall.productservice.dal.mysql.dataobject.attr.ProductAttrValueDO;
|
||||||
|
import cn.iocoder.mall.productservice.dal.mysql.mapper.attr.ProductAttrKeyMapper;
|
||||||
|
import cn.iocoder.mall.productservice.dal.mysql.mapper.attr.ProductAttrValueMapper;
|
||||||
|
import cn.iocoder.mall.productservice.service.attr.bo.ProductAttrKeyValueBO;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static cn.iocoder.mall.productservice.enums.ProductErrorCodeConstants.PRODUCT_ATTR_KEY_NOT_EXIST;
|
||||||
|
import static cn.iocoder.mall.productservice.enums.ProductErrorCodeConstants.PRODUCT_ATTR_VALUE_NOT_EXIST;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class ProductAttrService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ProductAttrKeyMapper productAttrKeyMapper;
|
||||||
|
@Autowired
|
||||||
|
private ProductAttrValueMapper productAttrValueMapper;
|
||||||
|
|
||||||
|
public List<ProductAttrKeyValueBO> validProductAttr(Set<Integer> productAttrValueIds, boolean validStatus) {
|
||||||
|
// 首先,校验规格 Value
|
||||||
|
List<ProductAttrValueDO> attrValues = productAttrValueMapper.selectBatchIds(productAttrValueIds);
|
||||||
|
if (attrValues.size() != productAttrValueIds.size()) {
|
||||||
|
throw ServiceExceptionUtil.exception(PRODUCT_ATTR_VALUE_NOT_EXIST);
|
||||||
|
}
|
||||||
|
if (validStatus) {
|
||||||
|
for (ProductAttrValueDO attrValue : attrValues) { // 同时,校验下状态
|
||||||
|
if (CommonStatusEnum.DISABLE.getValue().equals(attrValue.getStatus())) {
|
||||||
|
throw ServiceExceptionUtil.exception(PRODUCT_ATTR_VALUE_NOT_EXIST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 然后,校验规 Key
|
||||||
|
Set<Integer> attrKeyIds = CollectionUtils.convertSet(attrValues, ProductAttrValueDO::getAttrId);
|
||||||
|
List<ProductAttrKeyDO> attrKeys = productAttrKeyMapper.selectBatchIds(attrKeyIds);
|
||||||
|
if (attrKeys.size() != attrKeyIds.size()) {
|
||||||
|
throw ServiceExceptionUtil.exception(PRODUCT_ATTR_KEY_NOT_EXIST);
|
||||||
|
}
|
||||||
|
if (validStatus) {
|
||||||
|
for (ProductAttrKeyDO attrKey : attrKeys) { // 同时,校验下状态
|
||||||
|
if (CommonStatusEnum.DISABLE.getValue().equals(attrKey.getStatus())) {
|
||||||
|
throw ServiceExceptionUtil.exception(PRODUCT_ATTR_KEY_NOT_EXIST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 返回成功
|
||||||
|
Map<Integer, ProductAttrKeyDO> attrKeyMap = CollectionUtils.convertMap(attrKeys, ProductAttrKeyDO::getId, attrKeyDO -> attrKeyDO); // ProductAttrDO 的映射,方便查找。
|
||||||
|
return attrValues.stream().map(attrValueDO -> new ProductAttrKeyValueBO()
|
||||||
|
.setAttrKeyId(attrValueDO.getAttrId()).setAttrKeyName(attrKeyMap.get(attrValueDO.getAttrId()).getName())
|
||||||
|
.setAttrValueId(attrValueDO.getId()).setAttrValueName(attrValueDO.getName()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package cn.iocoder.mall.productservice.service.attr.bo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商品规格键值对 BO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class ProductAttrKeyValueBO implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 规格 Key 编号
|
||||||
|
*/
|
||||||
|
private Integer attrKeyId;
|
||||||
|
/**
|
||||||
|
* 规格 Key 名字
|
||||||
|
*/
|
||||||
|
private String attrKeyName;
|
||||||
|
/**
|
||||||
|
* 规格 Value 编号
|
||||||
|
*/
|
||||||
|
private Integer attrValueId;
|
||||||
|
/**
|
||||||
|
* 规格 Value 名字
|
||||||
|
*/
|
||||||
|
private String attrValueName;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
package cn.iocoder.mall.productservice.service.sku;
|
||||||
|
|
||||||
|
import cn.iocoder.common.framework.enums.CommonStatusEnum;
|
||||||
|
import cn.iocoder.common.framework.exception.util.ServiceExceptionUtil;
|
||||||
|
import cn.iocoder.mall.productservice.convert.sku.ProductSkuConvert;
|
||||||
|
import cn.iocoder.mall.productservice.dal.mysql.dataobject.spu.ProductSkuDO;
|
||||||
|
import cn.iocoder.mall.productservice.dal.mysql.mapper.sku.ProductSkuMapper;
|
||||||
|
import cn.iocoder.mall.productservice.service.attr.bo.ProductAttrKeyValueBO;
|
||||||
|
import cn.iocoder.mall.productservice.service.sku.bo.ProductSkuCreateOrUpdateBO;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static cn.iocoder.mall.productservice.enums.ProductErrorCodeConstants.*;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class ProductSkuService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ProductSkuMapper productSkuMapper;
|
||||||
|
|
||||||
|
public void createProductSkus(Integer spuId, List<ProductSkuCreateOrUpdateBO> createBOs) {
|
||||||
|
List<ProductSkuDO> skus = ProductSkuConvert.INSTANCE.convertList(createBOs);
|
||||||
|
skus.forEach(sku -> {
|
||||||
|
sku.setStatus(CommonStatusEnum.ENABLE.getValue());
|
||||||
|
sku.setSpuId(spuId);
|
||||||
|
});
|
||||||
|
productSkuMapper.insertList(skus);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateProductSkus(Integer spuId, List<ProductSkuCreateOrUpdateBO> createBOs) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验 sku 是否合法
|
||||||
|
*
|
||||||
|
* @param skuBOs 商品 SKU 添加信息
|
||||||
|
* @param attrKeyValueBOs 商品规格明细数组
|
||||||
|
*/
|
||||||
|
public void validProductSku(List<ProductSkuCreateOrUpdateBO> skuBOs, List<ProductAttrKeyValueBO> attrKeyValueBOs) {
|
||||||
|
// 创建 ProductAttrDetailBO 的映射。其中,KEY 为 ProductAttrDetailBO.attrValueId ,即规格值的编号
|
||||||
|
Map<Integer, ProductAttrKeyValueBO> productAttrDetailBOMap = attrKeyValueBOs.stream().collect(
|
||||||
|
Collectors.toMap(ProductAttrKeyValueBO::getAttrValueId, productAttrDetailBO -> productAttrDetailBO));
|
||||||
|
// 1. 先校验,一个 Sku 下,没有重复的规格。校验方式是,遍历每个 Sku ,看看是否有重复的规格 attrId
|
||||||
|
for (ProductSkuCreateOrUpdateBO sku : skuBOs) {
|
||||||
|
Set<Integer> attrIds = sku.getAttrValueIds().stream().map(attrValueId -> productAttrDetailBOMap.get(attrValueId).getAttrKeyId())
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
if (attrIds.size() != sku.getAttrValueIds().size()) {
|
||||||
|
throw ServiceExceptionUtil.exception(PRODUCT_SKU_ATTR_CANT_NOT_DUPLICATE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 2. 再校验,每个 Sku 的规格值的数量,是一致的。
|
||||||
|
int attrValueIdsSize = skuBOs.get(0).getAttrValueIds().size();
|
||||||
|
for (int i = 1; i < skuBOs.size(); i++) {
|
||||||
|
if (attrValueIdsSize != skuBOs.get(i).getAttrValueIds().size()) {
|
||||||
|
throw ServiceExceptionUtil.exception(PRODUCT_SPU_ATTR_NUMBERS_MUST_BE_EQUALS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 3. 最后校验,每个 Sku 之间不是重复的
|
||||||
|
Set<Set<Integer>> skuAttrValues = new HashSet<>(); // 每个元素,都是一个 Sku 的 attrValueId 集合。这样,通过最外层的 Set ,判断是否有重复的.
|
||||||
|
for (ProductSkuCreateOrUpdateBO sku : skuBOs) {
|
||||||
|
if (!skuAttrValues.add(new HashSet<>(sku.getAttrValueIds()))) { // 添加失败,说明重复
|
||||||
|
throw ServiceExceptionUtil.exception(PRODUCT_SPU_SKU_NOT_DUPLICATE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package cn.iocoder.mall.product.api.dto;
|
package cn.iocoder.mall.productservice.service.sku.bo;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
|
@ -8,17 +8,19 @@ import javax.validation.constraints.NotNull;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 商品 Sku 添加 DTO
|
* 商品 SKU 创建或者修改 BO
|
||||||
|
*
|
||||||
|
* 注意,目前该对象是
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@Accessors(chain = true)
|
@Accessors(chain = true)
|
||||||
public class ProductSkuAddOrUpdateDTO {
|
public class ProductSkuCreateOrUpdateBO {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 规格值数组
|
* 规格值数组
|
||||||
*/
|
*/
|
||||||
@NotNull(message = "规格值数组不能为空")
|
@NotNull(message = "规格值数组不能为空")
|
||||||
private List<Integer> attrs;
|
private List<Integer> attrValueIds;
|
||||||
/**
|
/**
|
||||||
* 价格,单位:分
|
* 价格,单位:分
|
||||||
*/
|
*/
|
|
@ -17,6 +17,14 @@ public class ProductSpuPageBO extends PageParam {
|
||||||
* SPU 名字
|
* SPU 名字
|
||||||
*/
|
*/
|
||||||
private String name;
|
private String name;
|
||||||
|
/**
|
||||||
|
* 分类编号
|
||||||
|
*/
|
||||||
|
private Integer cid;
|
||||||
|
/**
|
||||||
|
* 是否可见
|
||||||
|
*/
|
||||||
|
private Boolean visible;
|
||||||
/**
|
/**
|
||||||
* 是否有库存
|
* 是否有库存
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?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.productservice.dal.mysql.mapper.sku.ProductSkuMapper">
|
||||||
|
|
||||||
|
<insert id="insertList" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
|
||||||
|
INSERT INTO product_sku (
|
||||||
|
spu_id, status, pic_url, attrs, price,
|
||||||
|
quantity
|
||||||
|
) VALUES
|
||||||
|
<foreach collection="productSkuDOs" item="productSkuDO" separator=",">
|
||||||
|
(#{productSkuDO.spuId}, #{productSkuDO.status}, #{productSkuDO.picUrl}, #{productSkuDO.attrs}, #{productSkuDO.price},
|
||||||
|
#{productSkuDO.quantity}
|
||||||
|
)
|
||||||
|
</foreach>
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
</mapper>
|
|
@ -1,32 +0,0 @@
|
||||||
package cn.iocoder.mall.product.biz.bo.product;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.experimental.Accessors;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 商品规格明细 BO
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@Accessors(chain = true)
|
|
||||||
public class ProductAttrAndValuePairBO implements Serializable {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 规格编号
|
|
||||||
*/
|
|
||||||
private Integer attrId;
|
|
||||||
/**
|
|
||||||
* 规格名
|
|
||||||
*/
|
|
||||||
private String attrName;
|
|
||||||
/**
|
|
||||||
* 规格值
|
|
||||||
*/
|
|
||||||
private Integer attrValueId;
|
|
||||||
/**
|
|
||||||
* 规格值名
|
|
||||||
*/
|
|
||||||
private String attrValueName;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
package cn.iocoder.mall.product.biz.dao.attr;
|
|
||||||
|
|
||||||
import cn.iocoder.mall.product.biz.dataobject.attr.ProductAttrDO;
|
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
|
||||||
import org.springframework.stereotype.Repository;
|
|
||||||
|
|
||||||
@Repository
|
|
||||||
public interface ProductAttrMapper extends BaseMapper<ProductAttrDO> {
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
package cn.iocoder.mall.product.biz.dao.sku;
|
|
||||||
|
|
||||||
import cn.iocoder.mall.product.biz.dataobject.spu.ProductSpuDO;
|
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
|
||||||
import org.springframework.stereotype.Repository;
|
|
||||||
|
|
||||||
@Repository
|
|
||||||
public interface ProductSpuMapper extends BaseMapper<ProductSpuDO> {
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
package cn.iocoder.mall.product.biz.dataobject.attr;
|
|
||||||
|
|
||||||
import cn.iocoder.mall.mybatis.core.dataobject.DeletableDO;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.experimental.Accessors;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Product 规格
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@Accessors(chain = true)
|
|
||||||
public class ProductAttrDO extends DeletableDO {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 规格编号
|
|
||||||
*/
|
|
||||||
private Integer id;
|
|
||||||
/**
|
|
||||||
* 名称
|
|
||||||
*/
|
|
||||||
private String name;
|
|
||||||
/**
|
|
||||||
* 状态
|
|
||||||
*
|
|
||||||
* 1-开启
|
|
||||||
* 2-禁用
|
|
||||||
*/
|
|
||||||
private Integer status;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
package cn.iocoder.mall.product.biz.dto.product;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,62 +0,0 @@
|
||||||
package cn.iocoder.mall.product.biz.dto.product;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
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;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
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)
|
|
||||||
// TODO FROM 芋艿 to sunderui && q2118cs:貌似重复了,只要保留一个哈
|
|
||||||
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;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,69 +0,0 @@
|
||||||
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 +0,0 @@
|
||||||
<?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.ProductSpuMapper">
|
|
||||||
|
|
||||||
|
|
||||||
</mapper>
|
|
|
@ -22,8 +22,6 @@ public interface ProductSpuService {
|
||||||
*/
|
*/
|
||||||
List<ProductSpuDetailBO> getProductSpuDetailListForSync(Integer lastId, Integer limit);
|
List<ProductSpuDetailBO> getProductSpuDetailListForSync(Integer lastId, Integer limit);
|
||||||
|
|
||||||
ProductSpuPageBO getProductSpuPage(ProductSpuPageDTO productSpuPageDTO);
|
|
||||||
|
|
||||||
List<ProductSpuBO> getProductSpuSearchList(ProductSpuSearchListDTO productSpuSearchListDTO);
|
List<ProductSpuBO> getProductSpuSearchList(ProductSpuSearchListDTO productSpuSearchListDTO);
|
||||||
|
|
||||||
List<ProductSpuBO> getProductSpuList(Collection<Integer> ids);
|
List<ProductSpuBO> getProductSpuList(Collection<Integer> ids);
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
package cn.iocoder.mall.product.api.bo;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.experimental.Accessors;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 商品规格明细 BO
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@Accessors(chain = true)
|
|
||||||
public class ProductAttrAndValuePairBO implements Serializable {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 规格编号
|
|
||||||
*/
|
|
||||||
private Integer attrId;
|
|
||||||
/**
|
|
||||||
* 规格名
|
|
||||||
*/
|
|
||||||
private String attrName;
|
|
||||||
/**
|
|
||||||
* 规格值
|
|
||||||
*/
|
|
||||||
private Integer attrValueId;
|
|
||||||
/**
|
|
||||||
* 规格值名
|
|
||||||
*/
|
|
||||||
private String attrValueName;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,74 +0,0 @@
|
||||||
package cn.iocoder.mall.product.api.bo;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.experimental.Accessors;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 商品 SPU BO
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@Accessors(chain = true)
|
|
||||||
public class ProductSpuBO implements Serializable {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SPU 编号
|
|
||||||
*/
|
|
||||||
private Integer id;
|
|
||||||
|
|
||||||
// ========== 基本信息 =========
|
|
||||||
/**
|
|
||||||
* SPU 名字
|
|
||||||
*/
|
|
||||||
private String name;
|
|
||||||
/**
|
|
||||||
* 卖点
|
|
||||||
*/
|
|
||||||
private String sellPoint;
|
|
||||||
/**
|
|
||||||
* 描述
|
|
||||||
*/
|
|
||||||
private String description;
|
|
||||||
/**
|
|
||||||
* 分类编号
|
|
||||||
*/
|
|
||||||
private Integer cid;
|
|
||||||
/**
|
|
||||||
* 商品主图地址
|
|
||||||
*
|
|
||||||
* 数组,以逗号分隔
|
|
||||||
*
|
|
||||||
* 建议尺寸:800*800像素,你可以拖拽图片调整顺序,最多上传15张
|
|
||||||
*/
|
|
||||||
private List<String> picUrls;
|
|
||||||
|
|
||||||
// ========== 其他信息 =========
|
|
||||||
/**
|
|
||||||
* 是否上架商品(是否可见)。
|
|
||||||
*
|
|
||||||
* true 为已上架
|
|
||||||
* false 为已下架
|
|
||||||
*/
|
|
||||||
private Boolean visible;
|
|
||||||
/**
|
|
||||||
* 排序字段
|
|
||||||
*/
|
|
||||||
private Integer sort;
|
|
||||||
|
|
||||||
// ========== Sku 相关字段 =========
|
|
||||||
/**
|
|
||||||
* 价格
|
|
||||||
*
|
|
||||||
* 目前的计算方式是,以 Sku 最小价格为准
|
|
||||||
*/
|
|
||||||
private Integer price;
|
|
||||||
/**
|
|
||||||
* 库存数量
|
|
||||||
*
|
|
||||||
* 目前的计算方式是,以 Sku 库存累加为准
|
|
||||||
*/
|
|
||||||
private Integer quantity;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
package cn.iocoder.mall.product.api.bo;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.experimental.Accessors;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 商品 SPU 分页 BO
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@Accessors(chain = true)
|
|
||||||
public class ProductSpuPageBO implements Serializable {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Spu 数组
|
|
||||||
*/
|
|
||||||
private List<ProductSpuBO> list;
|
|
||||||
/**
|
|
||||||
* 总量
|
|
||||||
*/
|
|
||||||
private Integer total;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,62 +0,0 @@
|
||||||
package cn.iocoder.mall.product.api.dto;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
package cn.iocoder.mall.product.api.dto;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.experimental.Accessors;
|
|
||||||
|
|
||||||
import javax.validation.constraints.NotNull;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 商品 Spu 分页 DTO
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@Accessors(chain = true)
|
|
||||||
public class ProductSpuPageDTO {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 商品名
|
|
||||||
*
|
|
||||||
* 模糊匹配
|
|
||||||
*/
|
|
||||||
private String name;
|
|
||||||
/**
|
|
||||||
* 分类编号
|
|
||||||
*/
|
|
||||||
private Integer cid;
|
|
||||||
/**
|
|
||||||
* 是否可见
|
|
||||||
*/
|
|
||||||
private Boolean visible;
|
|
||||||
/**
|
|
||||||
* 是否有库存
|
|
||||||
*
|
|
||||||
* 允许为空。空时,不进行筛选
|
|
||||||
*/
|
|
||||||
private Boolean hasQuantity;
|
|
||||||
|
|
||||||
@NotNull(message = "页码不能为空")
|
|
||||||
private Integer pageNo;
|
|
||||||
@NotNull(message = "每页条数不能为空")
|
|
||||||
private Integer pageSize;
|
|
||||||
|
|
||||||
}
|
|
|
@ -10,10 +10,6 @@ import java.util.List;
|
||||||
@Repository
|
@Repository
|
||||||
public interface ProductSpuMapper {
|
public interface ProductSpuMapper {
|
||||||
|
|
||||||
ProductSpuDO selectById(Integer id);
|
|
||||||
|
|
||||||
List<ProductSpuDO> selectByIds(@Param("ids") Collection<Integer> ids);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得大于 id 的商品编号数组
|
* 获得大于 id 的商品编号数组
|
||||||
*
|
*
|
||||||
|
@ -24,21 +20,4 @@ public interface ProductSpuMapper {
|
||||||
List<Integer> selectIdListByIdGt(@Param("id") Integer id,
|
List<Integer> selectIdListByIdGt(@Param("id") Integer id,
|
||||||
@Param("limit") Integer limit);
|
@Param("limit") Integer limit);
|
||||||
|
|
||||||
void insert(ProductSpuDO productSpuDO);
|
|
||||||
|
|
||||||
void update(ProductSpuDO productSpuDO);
|
|
||||||
|
|
||||||
// TODO 芋艿,需要捉摸下,怎么优化下。参数有点多
|
|
||||||
List<ProductSpuDO> selectListByNameLikeOrderBySortAsc(@Param("name") String name,
|
|
||||||
@Param("cid") Integer cid,
|
|
||||||
@Param("visible") Boolean visible,
|
|
||||||
@Param("hasQuantity") Boolean hasQuantity,
|
|
||||||
@Param("offset") Integer offset,
|
|
||||||
@Param("limit") Integer limit);
|
|
||||||
|
|
||||||
Integer selectCountByNameLike(@Param("name") String name,
|
|
||||||
@Param("cid") Integer cid,
|
|
||||||
@Param("hasQuantity") Boolean hasQuantity,
|
|
||||||
@Param("visible") Boolean visible);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,39 +35,6 @@ public class ProductAttrServiceImpl implements ProductAttrService {
|
||||||
@Autowired
|
@Autowired
|
||||||
private ProductAttrValueMapper productAttrValueMapper;
|
private ProductAttrValueMapper productAttrValueMapper;
|
||||||
|
|
||||||
public List<ProductAttrAndValuePairBO> validProductAttrAndValue(Set<Integer> productAttrValueIds, boolean validStatus) {
|
|
||||||
// 首先,校验规格值
|
|
||||||
List<ProductAttrValueDO> attrValues = productAttrValueMapper.selectListByIds(productAttrValueIds);
|
|
||||||
if (attrValues.size() != productAttrValueIds.size()) {
|
|
||||||
throw ServiceExceptionUtil.exception(ProductErrorCodeEnum.PRODUCT_ATTR_VALUE_NOT_EXIST.getCode());
|
|
||||||
}
|
|
||||||
if (validStatus) {
|
|
||||||
for (ProductAttrValueDO attrValue : attrValues) { // 同时,校验下状态
|
|
||||||
if (ProductAttrConstants.ATTR_STATUS_DISABLE.equals(attrValue.getStatus())) {
|
|
||||||
throw ServiceExceptionUtil.exception(ProductErrorCodeEnum.PRODUCT_ATTR_VALUE_NOT_EXIST.getCode());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 然后,校验规格
|
|
||||||
Set<Integer> attrIds = attrValues.stream().map(ProductAttrValueDO::getAttrId).collect(Collectors.toSet());
|
|
||||||
List<ProductAttrDO> attrs = productAttrMapper.selectListByIds(attrIds);
|
|
||||||
if (attrs.size() != attrIds.size()) {
|
|
||||||
throw ServiceExceptionUtil.exception(ProductErrorCodeEnum.PRODUCT_ATTR_NOT_EXIST.getCode());
|
|
||||||
}
|
|
||||||
if (validStatus) {
|
|
||||||
for (ProductAttrDO attr : attrs) { // 同时,校验下状态
|
|
||||||
if (ProductAttrConstants.ATTR_VALUE_STATUS_DISABLE.equals(attr.getStatus())) {
|
|
||||||
throw ServiceExceptionUtil.exception(ProductErrorCodeEnum.PRODUCT_ATTR_NOT_EXIST.getCode());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 返回成功
|
|
||||||
Map<Integer, ProductAttrDO> attrMap = attrs.stream().collect(Collectors.toMap(ProductAttrDO::getId, productAttrDO -> productAttrDO)); // ProductAttrDO 的映射,方便查找。
|
|
||||||
return attrValues.stream().map(productAttrValueDO -> new ProductAttrAndValuePairBO()
|
|
||||||
.setAttrId(productAttrValueDO.getAttrId()).setAttrName(attrMap.get(productAttrValueDO.getAttrId()).getName())
|
|
||||||
.setAttrValueId(productAttrValueDO.getId()).setAttrValueName(productAttrValueDO.getName())).collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ProductAttrPageBO getProductAttrPage(ProductAttrPageDTO productAttrPageDTO) {
|
public ProductAttrPageBO getProductAttrPage(ProductAttrPageDTO productAttrPageDTO) {
|
||||||
ProductAttrPageBO productAttrPageBO = new ProductAttrPageBO();
|
ProductAttrPageBO productAttrPageBO = new ProductAttrPageBO();
|
||||||
|
|
|
@ -96,45 +96,6 @@ public class ProductSpuServiceImpl implements ProductSpuService {
|
||||||
return productSpuDetailBO;
|
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.convert(productSpuAddDTO)
|
|
||||||
.setPicUrls(StringUtil.join(productSpuAddDTO.getPicUrls(), ","))
|
|
||||||
.setSort(0); // 排序为 0
|
|
||||||
spu.setCreateTime(new Date());
|
|
||||||
spu.setDeleted(DeletedStatusEnum.DELETED_NO.getValue());
|
|
||||||
initSpuFromSkus(spu, productSpuAddDTO.getSkus()); // 初始化 sku 相关信息到 spu 中
|
|
||||||
productSpuMapper.insert(spu);
|
|
||||||
// 保存 Sku
|
|
||||||
List<ProductSkuDO> skus = productSpuAddDTO.getSkus().stream().map(productSkuAddDTO -> {
|
|
||||||
ProductSkuDO sku = ProductSpuConvert.INSTANCE.convert(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);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateProductSpu(Integer adminId, ProductSpuUpdateDTO productSpuUpdateDTO) {
|
public void updateProductSpu(Integer adminId, ProductSpuUpdateDTO productSpuUpdateDTO) {
|
||||||
// 更新商品
|
// 更新商品
|
||||||
|
@ -276,43 +237,6 @@ public class ProductSpuServiceImpl implements ProductSpuService {
|
||||||
return ProductSpuConvert.INSTANCE.convert3(skus, spus, attrAndValuePairList);
|
return ProductSpuConvert.INSTANCE.convert3(skus, spus, attrAndValuePairList);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 校验 sku 是否合法
|
|
||||||
*
|
|
||||||
* @param productSkuAddDTOs sku 添加或修改信息
|
|
||||||
* @param productAttrDetailBOs 商品规格明细数组
|
|
||||||
* @return 是否校验通过
|
|
||||||
*/
|
|
||||||
private Boolean 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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 校验通过
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得 sku 数组中,指定规格的 sku
|
* 获得 sku 数组中,指定规格的 sku
|
||||||
*
|
*
|
||||||
|
@ -335,18 +259,6 @@ public class ProductSpuServiceImpl implements ProductSpuService {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据 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) {
|
private boolean sendProductUpdateMessage(Integer id) {
|
||||||
// 创建 Message 对象
|
// 创建 Message 对象
|
||||||
ProductUpdateMessage message = new ProductUpdateMessage().setId(id);
|
ProductUpdateMessage message = new ProductUpdateMessage().setId(id);
|
||||||
|
|
|
@ -8,25 +8,6 @@
|
||||||
create_time
|
create_time
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
<select id="selectById" parameterType="Integer" resultType="ProductSpuDO">
|
|
||||||
SELECT
|
|
||||||
<include refid="FIELDS" />
|
|
||||||
FROM product_spu
|
|
||||||
WHERE id = #{id}
|
|
||||||
AND deleted = 0
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<select id="selectByIds" resultType="ProductSpuDO">
|
|
||||||
SELECT
|
|
||||||
<include refid="FIELDS" />
|
|
||||||
FROM product_spu
|
|
||||||
WHERE id IN
|
|
||||||
<foreach item="id" collection="ids" separator="," open="(" close=")" index="">
|
|
||||||
#{id}
|
|
||||||
</foreach>
|
|
||||||
AND deleted = 0
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<select id="selectIdListByIdGt" parameterType="Integer" resultType="Integer">
|
<select id="selectIdListByIdGt" parameterType="Integer" resultType="Integer">
|
||||||
SELECT
|
SELECT
|
||||||
<include refid="FIELDS" />
|
<include refid="FIELDS" />
|
||||||
|
@ -41,105 +22,4 @@
|
||||||
LIMIT #{limit}
|
LIMIT #{limit}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<insert id="insert" parameterType="ProductSpuDO" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
|
|
||||||
INSERT INTO product_spu (
|
|
||||||
name, sell_point, description, cid, pic_urls,
|
|
||||||
visible, sort, price, quantity,
|
|
||||||
deleted, create_time
|
|
||||||
) VALUES (
|
|
||||||
#{name}, #{sellPoint}, #{description}, #{cid}, #{picUrls},
|
|
||||||
#{visible}, #{sort}, #{price}, #{quantity},
|
|
||||||
#{deleted}, #{createTime}
|
|
||||||
)
|
|
||||||
</insert>
|
|
||||||
|
|
||||||
<update id="update" parameterType="ProductSpuDO">
|
|
||||||
UPDATE product_spu
|
|
||||||
<set>
|
|
||||||
<if test="name != null">
|
|
||||||
name = #{name},
|
|
||||||
</if>
|
|
||||||
<if test="sellPoint != null">
|
|
||||||
sell_point = #{sellPoint},
|
|
||||||
</if>
|
|
||||||
<if test="description != null">
|
|
||||||
description = #{description},
|
|
||||||
</if>
|
|
||||||
<if test="cid != null">
|
|
||||||
cid = #{cid},
|
|
||||||
</if>
|
|
||||||
<if test="picUrls != null">
|
|
||||||
pic_urls = #{picUrls},
|
|
||||||
</if>
|
|
||||||
<if test="visible != null">
|
|
||||||
visible = #{visible},
|
|
||||||
</if>
|
|
||||||
<if test="price != null">
|
|
||||||
price = #{price},
|
|
||||||
</if>
|
|
||||||
<if test="quantity != null">
|
|
||||||
quantity = #{quantity},
|
|
||||||
</if>
|
|
||||||
<if test="sort != null">
|
|
||||||
sort = #{sort},
|
|
||||||
</if>
|
|
||||||
<if test="deleted != null">
|
|
||||||
deleted = #{deleted}
|
|
||||||
</if>
|
|
||||||
</set>
|
|
||||||
WHERE id = #{id}
|
|
||||||
</update>
|
|
||||||
|
|
||||||
<select id="selectListByNameLikeOrderBySortAsc" resultType="ProductSpuDO">
|
|
||||||
SELECT
|
|
||||||
<include refid="FIELDS" />
|
|
||||||
FROM product_spu
|
|
||||||
<where>
|
|
||||||
<if test="name != null">
|
|
||||||
name LIKE "%"#{name}"%"
|
|
||||||
</if>
|
|
||||||
<if test="cid != null">
|
|
||||||
AND cid = #{cid}
|
|
||||||
</if>
|
|
||||||
<if test="visible != null">
|
|
||||||
AND visible = #{visible}
|
|
||||||
</if>
|
|
||||||
<if test="hasQuantity == true">
|
|
||||||
AND quantity > 0
|
|
||||||
</if>
|
|
||||||
<if test="hasQuantity == false">
|
|
||||||
AND quantity = 0
|
|
||||||
</if>
|
|
||||||
AND deleted = 0
|
|
||||||
</where>
|
|
||||||
ORDER BY sort ASC
|
|
||||||
<if test="offset != null and limit != null">
|
|
||||||
LIMIT #{offset}, #{limit}
|
|
||||||
</if>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<select id="selectCountByNameLike" resultType="Integer">
|
|
||||||
SELECT
|
|
||||||
COUNT(1)
|
|
||||||
FROM product_spu
|
|
||||||
<where>
|
|
||||||
<if test="name != null">
|
|
||||||
name LIKE "%"#{name}"%"
|
|
||||||
</if>
|
|
||||||
<if test="cid != null">
|
|
||||||
AND cid = #{cid}
|
|
||||||
</if>
|
|
||||||
<if test="visible != null">
|
|
||||||
AND visible = #{visible}
|
|
||||||
</if>
|
|
||||||
<if test="hasQuantity == true">
|
|
||||||
AND quantity > 0
|
|
||||||
</if>
|
|
||||||
<if test="hasQuantity == false">
|
|
||||||
AND quantity = 0
|
|
||||||
</if>
|
|
||||||
AND deleted = 0
|
|
||||||
</where>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
<?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.dao.UserProductSpuCollectionsMapper">
|
|
||||||
|
|
||||||
<sql id="FIELDS">
|
|
||||||
id, user_id, nickname, spu_id, spu_name,
|
|
||||||
spu_image,sell_point,price, create_time, update_time,
|
|
||||||
deleted
|
|
||||||
</sql>
|
|
||||||
|
|
||||||
<select id="selectById" parameterType="Integer" resultType="UserProductSpuCollectionsDO">
|
|
||||||
SELECT
|
|
||||||
<include refid="FIELDS" />
|
|
||||||
FROM user_spu_collections
|
|
||||||
WHERE id = #{id}
|
|
||||||
AND deleted = 0
|
|
||||||
</select>
|
|
||||||
|
|
||||||
|
|
||||||
<select id="selectListByUser" resultType="UserProductSpuCollectionsDO">
|
|
||||||
SELECT
|
|
||||||
<include refid="FIELDS" />
|
|
||||||
FROM user_spu_collections
|
|
||||||
<where>
|
|
||||||
user_id = #{userId} AND deleted = 0
|
|
||||||
</where>
|
|
||||||
<if test="offset != null and limit != null">
|
|
||||||
LIMIT #{offset}, #{limit}
|
|
||||||
</if>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<select id="selectCountByUser" resultType="Integer">
|
|
||||||
SELECT
|
|
||||||
COUNT(1)
|
|
||||||
FROM user_spu_collections
|
|
||||||
<where>
|
|
||||||
user_id = #{userId} AND deleted = 0
|
|
||||||
</where>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
</mapper>
|
|
|
@ -1,19 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
|
||||||
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
|
|
||||||
<configuration>
|
|
||||||
|
|
||||||
<settings>
|
|
||||||
<!-- 使用驼峰命名法转换字段。 -->
|
|
||||||
<setting name="mapUnderscoreToCamelCase" value="true"/>
|
|
||||||
</settings>
|
|
||||||
|
|
||||||
<typeAliases>
|
|
||||||
<typeAlias alias="Integer" type="java.lang.Integer"/>
|
|
||||||
<typeAlias alias="Long" type="java.lang.Long"/>
|
|
||||||
<typeAlias alias="HashMap" type="java.util.HashMap"/>
|
|
||||||
<typeAlias alias="LinkedHashMap" type="java.util.LinkedHashMap"/>
|
|
||||||
<typeAlias alias="ArrayList" type="java.util.ArrayList"/>
|
|
||||||
<typeAlias alias="LinkedList" type="java.util.LinkedList"/>
|
|
||||||
</typeAliases>
|
|
||||||
|
|
||||||
</configuration>
|
|
Loading…
Reference in New Issue