diff --git a/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/convert/product/ProductSpuConvert.java b/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/convert/product/ProductSpuConvert.java index e6b4ec24a..4c7ea79a0 100644 --- a/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/convert/product/ProductSpuConvert.java +++ b/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/convert/product/ProductSpuConvert.java @@ -130,6 +130,4 @@ public interface ProductSpuConvert { return spuDetailList; } - - } diff --git a/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/dao/product/ProductSkuMapper.java b/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/dao/product/ProductSkuMapper.java index acd271ed4..f0df9a044 100644 --- a/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/dao/product/ProductSkuMapper.java +++ b/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/dao/product/ProductSkuMapper.java @@ -1,10 +1,19 @@ package cn.iocoder.mall.product.biz.dao.product; -import cn.iocoder.mall.product.biz.dataobject.product.ProductBrandDO; +import cn.iocoder.mall.product.biz.dataobject.product.ProductSkuDO; import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; import org.springframework.stereotype.Repository; -@Repository -public interface ProductSkuMapper extends BaseMapper { +import java.util.List; +@Repository +public interface ProductSkuMapper extends BaseMapper { + + default List selectListBySpuIdAndStatus(Integer spuId, Integer status) { + return selectList(Wrappers.query().lambda() + .eq(ProductSkuDO::getSpuId, spuId) + .eq(ProductSkuDO::getStatus, status) + .eq(ProductSkuDO::getDeleted, false)); + } } diff --git a/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/dao/product/ProductSpuMapper.java b/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/dao/product/ProductSpuMapper.java index 21a7fd8f7..eab4e069a 100644 --- a/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/dao/product/ProductSpuMapper.java +++ b/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/dao/product/ProductSpuMapper.java @@ -1,11 +1,11 @@ package cn.iocoder.mall.product.biz.dao.product; -import cn.iocoder.mall.product.biz.dataobject.product.ProductBrandDO; +import cn.iocoder.mall.product.biz.dataobject.product.ProductSpuDO; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.springframework.stereotype.Repository; @Repository -public interface ProductSpuMapper extends BaseMapper { +public interface ProductSpuMapper extends BaseMapper { } diff --git a/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/service/product/ProductAttrService.java b/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/service/product/ProductAttrService.java index 1b81ac641..ccccac871 100644 --- a/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/service/product/ProductAttrService.java +++ b/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/service/product/ProductAttrService.java @@ -7,9 +7,11 @@ import cn.iocoder.mall.product.biz.bo.attr.ProductAttrBO; import cn.iocoder.mall.product.biz.bo.attr.ProductAttrSimpleWithValueBO; import cn.iocoder.mall.product.biz.bo.attr.ProductAttrValueBO; import cn.iocoder.mall.product.biz.bo.attr.ProductAttrWithValueBO; +import cn.iocoder.mall.product.biz.bo.product.ProductAttrAndValuePairBO; import cn.iocoder.mall.product.biz.dto.attr.*; import java.util.List; +import java.util.Set; public interface ProductAttrService { /** @@ -64,4 +66,5 @@ public interface ProductAttrService { Boolean updateProductAttrValueStatus(Integer adminId, Integer productAttrValueId, @InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}") Integer status); + List validProductAttrAndValue(Set productAttrValueIds, boolean validStatus); } diff --git a/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/service/product/ProductSpuService.java b/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/service/product/ProductSpuService.java new file mode 100644 index 000000000..696ae9e5d --- /dev/null +++ b/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/service/product/ProductSpuService.java @@ -0,0 +1,15 @@ +package cn.iocoder.mall.product.biz.service.product; + + +import cn.iocoder.mall.product.biz.bo.product.ProductSpuDetailBO; + +public interface ProductSpuService { + /** + * 获取SPU明细 + * + * @param id spuId + * @return SPU明细 + */ + ProductSpuDetailBO getProductSpuDetail(Integer id); + +} diff --git a/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/service/product/impl/ProductAttrServiceImpl.java b/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/service/product/impl/ProductAttrServiceImpl.java index 4199697c1..7abfb38d3 100644 --- a/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/service/product/impl/ProductAttrServiceImpl.java +++ b/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/service/product/impl/ProductAttrServiceImpl.java @@ -204,6 +204,7 @@ public class ProductAttrServiceImpl implements ProductAttrService { return i > 0; } + @Override public List validProductAttrAndValue(Set productAttrValueIds, boolean validStatus) { // 首先,校验规格值 List attrValues = productAttrValueMapper.selectBatchIds(productAttrValueIds); diff --git a/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/service/product/impl/ProductSpuServiceImpl.java b/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/service/product/impl/ProductSpuServiceImpl.java new file mode 100644 index 000000000..4bbe44599 --- /dev/null +++ b/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/service/product/impl/ProductSpuServiceImpl.java @@ -0,0 +1,59 @@ +package cn.iocoder.mall.product.biz.service.product.impl; + +import cn.iocoder.common.framework.util.ServiceExceptionUtil; +import cn.iocoder.common.framework.util.StringUtil; +import cn.iocoder.mall.product.biz.bo.product.ProductAttrAndValuePairBO; +import cn.iocoder.mall.product.biz.bo.product.ProductSpuDetailBO; +import cn.iocoder.mall.product.biz.convert.product.ProductSpuConvert; +import cn.iocoder.mall.product.biz.dao.product.ProductCategoryMapper; +import cn.iocoder.mall.product.biz.dao.product.ProductSkuMapper; +import cn.iocoder.mall.product.biz.dao.product.ProductSpuMapper; +import cn.iocoder.mall.product.biz.dataobject.product.ProductCategoryDO; +import cn.iocoder.mall.product.biz.dataobject.product.ProductSkuDO; +import cn.iocoder.mall.product.biz.dataobject.product.ProductSpuDO; +import cn.iocoder.mall.product.biz.enums.ProductErrorCodeEnum; +import cn.iocoder.mall.product.biz.enums.product.ProductSpuConstants; +import cn.iocoder.mall.product.biz.service.product.ProductAttrService; +import cn.iocoder.mall.product.biz.service.product.ProductSpuService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.Assert; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@Service +public class ProductSpuServiceImpl implements ProductSpuService { + + @Autowired + private ProductSpuMapper productSpuMapper; + @Autowired + private ProductSkuMapper productSkuMapper; + @Autowired + private ProductCategoryMapper productCategoryMapper; + @Autowired + private ProductAttrService productAttrService; + + @Override + public ProductSpuDetailBO getProductSpuDetail(Integer spuId) { + // 校验商品 spu 存在 + ProductSpuDO spu = productSpuMapper.selectById(spuId); + if (spu == null) { + throw ServiceExceptionUtil.exception(ProductErrorCodeEnum.PRODUCT_SPU_NOT_EXISTS.getCode()); + } + // 获得商品分类分类 + ProductCategoryDO category = productCategoryMapper.selectById(spu.getCid()); + Assert.notNull(category, String.format("分类编号(%d) 对应", spu.getCid())); + // 获得商品 sku 数组 + List skus = productSkuMapper.selectListBySpuIdAndStatus(spuId, ProductSpuConstants.SKU_STATUS_ENABLE); + // 获得规格 + Set productAttrValueIds = new HashSet<>(); + skus.forEach(sku -> productAttrValueIds.addAll(StringUtil.splitToInt(sku.getAttrs(), ","))); + // 读取规格时,不考虑规格是否被禁用 + List attrAndValuePairList = productAttrService.validProductAttrAndValue(productAttrValueIds, false); + // 返回成功 + return ProductSpuConvert.INSTANCE.convert2(spu, skus, attrAndValuePairList, category); + } + +} diff --git a/product/product-rpc-api/src/main/java/cn/iocoder/mall/product/rpc/api/ProductSpuRpc.java b/product/product-rpc-api/src/main/java/cn/iocoder/mall/product/rpc/api/ProductSpuRpc.java new file mode 100644 index 000000000..8948cdf37 --- /dev/null +++ b/product/product-rpc-api/src/main/java/cn/iocoder/mall/product/rpc/api/ProductSpuRpc.java @@ -0,0 +1,12 @@ +package cn.iocoder.mall.product.rpc.api; + +import cn.iocoder.mall.product.rpc.response.ProductSpuDetailResponse; + +/** + * @author Rai + */ +public interface ProductSpuRpc { + + ProductSpuDetailResponse getProductSpuDetail(Integer spuId); + +} diff --git a/product/product-rpc-api/src/main/java/cn/iocoder/mall/product/rpc/response/ProductSpuDetailResponse.java b/product/product-rpc-api/src/main/java/cn/iocoder/mall/product/rpc/response/ProductSpuDetailResponse.java new file mode 100644 index 000000000..3122bf1f3 --- /dev/null +++ b/product/product-rpc-api/src/main/java/cn/iocoder/mall/product/rpc/response/ProductSpuDetailResponse.java @@ -0,0 +1,127 @@ +package cn.iocoder.mall.product.rpc.response; + +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.List; + +/** + * 商品 Spu 明细 BO(包括 Sku 明细) + */ +@Data +@Accessors(chain = true) +public class ProductSpuDetailResponse implements Serializable { + + /** + * SPU 编号 + */ + private Integer id; + + // ========== 基本信息 ========= + /** + * SPU 名字 + */ + private String name; + /** + * 卖点 + */ + private String sellPoint; + /** + * 描述 + */ + private String description; + /** + * 分类编号 + */ + private Integer cid; + /** + * 分类名 + */ + private String categoryName; + /** + * 商品主图地址 + *

+ * 数组,以逗号分隔 + *

+ * 建议尺寸:800*800像素,你可以拖拽图片调整顺序,最多上传15张 + */ + private List picUrls; + + // ========== 其他信息 ========= + /** + * 是否上架商品(是否可见)。 + *

+ * true 为已上架 + * false 为已下架 + */ + private Boolean visible; + /** + * 排序字段 + */ + private Integer sort; + + // ========== SKU ========= + + /** + * SKU 数组 + */ + private List skus; + + /** + * 商品 Sku 明细 BO + */ + @Data + @Accessors(chain = true) + public static class Sku implements Serializable { + + /** + * sku 编号 + */ + private Integer id; + /** + * 商品编号 + */ + private Integer spuId; + /** + * 图片地址 + */ + private String picURL; + /** + * 规格值数组 + */ + private List attrs; + /** + * 价格,单位:分 + */ + private Integer price; + /** + * 库存数量 + */ + private Integer quantity; + + } + + @Data + @Accessors(chain = true) + public static class ProductAttrAndValuePair implements Serializable { + + /** + * 规格编号 + */ + private Integer attrId; + /** + * 规格名 + */ + private String attrName; + /** + * 规格值 + */ + private Integer attrValueId; + /** + * 规格值名 + */ + private String attrValueName; + + } +} diff --git a/product/product-rpc/src/main/java/cn/iocoder/mall/product/rpc/convert/ProductSpuConvert.java b/product/product-rpc/src/main/java/cn/iocoder/mall/product/rpc/convert/ProductSpuConvert.java new file mode 100644 index 000000000..23f2e38eb --- /dev/null +++ b/product/product-rpc/src/main/java/cn/iocoder/mall/product/rpc/convert/ProductSpuConvert.java @@ -0,0 +1,16 @@ +package cn.iocoder.mall.product.rpc.convert; + +import cn.iocoder.mall.product.biz.bo.product.ProductSpuDetailBO; +import cn.iocoder.mall.product.rpc.response.ProductSpuDetailResponse; +import org.mapstruct.Mapper; +import org.mapstruct.Mappings; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface ProductSpuConvert { + + ProductSpuConvert INSTANCE = Mappers.getMapper(ProductSpuConvert.class); + + @Mappings({}) + ProductSpuDetailResponse convertDetail(ProductSpuDetailBO productSpuDetail); +} diff --git a/product/product-rpc/src/main/java/cn/iocoder/mall/product/rpc/rpc/ProductSpuRpcImpl.java b/product/product-rpc/src/main/java/cn/iocoder/mall/product/rpc/rpc/ProductSpuRpcImpl.java new file mode 100644 index 000000000..04af31b0c --- /dev/null +++ b/product/product-rpc/src/main/java/cn/iocoder/mall/product/rpc/rpc/ProductSpuRpcImpl.java @@ -0,0 +1,24 @@ +package cn.iocoder.mall.product.rpc.rpc; + +import cn.iocoder.mall.product.biz.bo.product.ProductSpuDetailBO; +import cn.iocoder.mall.product.biz.service.product.ProductSpuService; +import cn.iocoder.mall.product.rpc.api.ProductSpuRpc; +import cn.iocoder.mall.product.rpc.convert.ProductSpuConvert; +import cn.iocoder.mall.product.rpc.response.ProductSpuDetailResponse; +import org.apache.dubbo.config.annotation.Service; +import org.springframework.beans.factory.annotation.Autowired; + +@Service(validation = "true", version = "${dubbo.provider.ProductSpuService.version}") +public class ProductSpuRpcImpl implements ProductSpuRpc { + + @Autowired + private ProductSpuService productSpuService; + + @Override + public ProductSpuDetailResponse getProductSpuDetail(Integer spuId) { + ProductSpuDetailBO productSpuDetail = productSpuService.getProductSpuDetail(spuId); + return ProductSpuConvert.INSTANCE.convertDetail(productSpuDetail); + } + + +} diff --git a/product/product-rpc/src/main/resources/rpc.yaml b/product/product-rpc/src/main/resources/rpc.yaml index 48d16c48a..8cacacf0a 100644 --- a/product/product-rpc/src/main/resources/rpc.yaml +++ b/product/product-rpc/src/main/resources/rpc.yaml @@ -2,39 +2,21 @@ dubbo: # Spring Cloud Alibaba Dubbo 专属配置 cloud: - subscribed-services: 'system-application' # 设置订阅的应用列表,默认为 * 订阅所有应用 + subscribed-services: 'product-application' # 设置订阅的应用列表,默认为 * 订阅所有应用 # Dubbo 提供者的协议 protocol: name: dubbo port: -1 # Dubbo 提供服务的扫描基础包 scan: - base-packages: cn.iocoder.mall.system.rpc.rpc + base-packages: cn.iocoder.mall.product.rpc.rpc # Dubbo 服务提供者的配置 provider: filter: -exception - SystemLogRPC: - version: 1.0.0 - OAuth2RPC: - version: 1.0.0 - AuthorizationRPC: - version: 1.0.0 - AdminRPC: - version: 1.0.0 - UserRPC: - version: 1.0.0 - UserAddressRPC: + ProductSpuService: version: 1.0.0 # Dubbo 服务消费者的配置 consumer: - SystemLogRPC: # 用于 AccessLogInterceptor 等拦截器,记录 HTTP API 请求的访问日志 - version: 1.0.0 - OAuth2RPC: # 用于 AccountAuthInterceptor 拦截器,执行认证 - version: 1.0.0 - AuthorizationRPC: # 用于 AccountAuthInterceptor 拦截器,执行鉴权(权限验证) - version: 1.0.0 - AdminRPC: - version: 1.0.0 - UserRPC: + ProductSpuService: version: 1.0.0 diff --git a/promotion/promotion-service-impl/pom.xml b/promotion/promotion-service-impl/pom.xml index 49b3d9bf7..7156cbe50 100644 --- a/promotion/promotion-service-impl/pom.xml +++ b/promotion/promotion-service-impl/pom.xml @@ -25,7 +25,7 @@ cn.iocoder.mall - product-service-api + product-rpc-api 1.0-SNAPSHOT diff --git a/promotion/promotion-service-impl/src/main/java/cn/iocoder/mall/promotion/biz/service/ProductRecommendServiceImpl.java b/promotion/promotion-service-impl/src/main/java/cn/iocoder/mall/promotion/biz/service/ProductRecommendServiceImpl.java index f9817416b..4e0a728f6 100644 --- a/promotion/promotion-service-impl/src/main/java/cn/iocoder/mall/promotion/biz/service/ProductRecommendServiceImpl.java +++ b/promotion/promotion-service-impl/src/main/java/cn/iocoder/mall/promotion/biz/service/ProductRecommendServiceImpl.java @@ -3,7 +3,7 @@ package cn.iocoder.mall.promotion.biz.service; import cn.iocoder.common.framework.constant.CommonStatusEnum; import cn.iocoder.common.framework.util.ServiceExceptionUtil; import cn.iocoder.mall.mybatis.enums.DeletedStatusEnum; -import cn.iocoder.mall.product.api.ProductSpuService; +import cn.iocoder.mall.product.rpc.api.ProductSpuRpc; import cn.iocoder.mall.promotion.api.ProductRecommendService; import cn.iocoder.mall.promotion.api.bo.ProductRecommendBO; import cn.iocoder.mall.promotion.api.bo.ProductRecommendPageBO; @@ -26,7 +26,7 @@ import java.util.List; public class ProductRecommendServiceImpl implements ProductRecommendService { @Reference(validation = "true", version = "${dubbo.consumer.ProductSpuService.version}") - private ProductSpuService productSpuService; + private ProductSpuRpc productSpuRpc; @Autowired private ProductRecommendMapper productRecommendMapper; @@ -52,7 +52,7 @@ public class ProductRecommendServiceImpl implements ProductRecommendService { @Override public ProductRecommendBO addProductRecommend(Integer adminId, ProductRecommendAddDTO productRecommendAddDTO) { // 校验商品不存在 - if (productSpuService.getProductSpuDetail(productRecommendAddDTO.getProductSpuId()) == null) { + if (productSpuRpc.getProductSpuDetail(productRecommendAddDTO.getProductSpuId()) == null) { throw ServiceExceptionUtil.exception(PromotionErrorCodeEnum.PRODUCT_RECOMMEND_PRODUCT_NOT_EXISTS.getCode()); } // 校验商品是否已经推荐 @@ -74,7 +74,7 @@ public class ProductRecommendServiceImpl implements ProductRecommendService { throw ServiceExceptionUtil.exception(PromotionErrorCodeEnum.PRODUCT_RECOMMEND_NOT_EXISTS.getCode()); } // 校验商品不存在 - if (productSpuService.getProductSpuDetail(productRecommendUpdateDTO.getProductSpuId()) == null) { + if (productSpuRpc.getProductSpuDetail(productRecommendUpdateDTO.getProductSpuId()) == null) { throw ServiceExceptionUtil.exception(PromotionErrorCodeEnum.PRODUCT_RECOMMEND_PRODUCT_NOT_EXISTS.getCode()); } // 校验商品是否已经推荐