# 商品详情页 # 一、服务搭建 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/b779805cebe24b8a900fb34f35fba68e.png) ## 1.配置host文件   我们的商品详情服务是一个独立的服务,我们需要在客户端的host中来配置映射 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/9a9442d1d28c4be98a56a8558dc40a54.png) ## 2.在Nginx中配置   我们需要在Nginx中配置商品详情服务的反向代理和静态资源的管理,首先看反向代理的配置 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/a6cdce0df44c4b9593cc9659959c30f6.png)   然后就是静态资源的管理,我们在es和商城首页的资源的同级目录下创建一个item目录,其中保存的就是商品详情页中的资源 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/9697da3555b04fce8099d71f7a87564f.png) ## 3.网关服务路由   当Nginx方向代理到了网关服务后,网关服务得跟进host路由到商品服务,我们得修改对应的配置信息 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/a0c0ae26af13460b834aa5414eefe446.png) ## 4.商品服务处理   到请求到了商品服务中我们需要接收请求,并且跳转到商品详情页中。 ```java /** * 商品详情的控制器 */ @Controller public class ItemController { @GetMapping("/{skuId}.html") public String item(@PathVariable Long skuId){ System.out.println(skuId); return "item"; } } ``` ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/5557722db79c4210b7035a91d77abf14.png) item.html页面中的内容在给大家的资源中有,拷贝进去即可,注意要修改相关的资源的路径 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/78f953f237964ccf93ee96a2029ca2eb.png) ## 5.检索服务跳转   商品详情服务搭建好了之后,我们需要通过商品的检索服务调整过来,那么我们需要修改跳转的地址信息 ```html

``` 最终的商品详情页的展示效果 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/1d13771f64b243f0a466e2fad930169e.png) # 二、商品详情数据 ## 1.响应VO封装   根据SKUID查询对应的商品详情信息,我们需要把对应的信息封装到对应的VO中,我们需要自己来封装该对象。 ```java /** * 商品详情页的数据对象 */ @Data public class SpuItemVO { // 1.sku的基本信息 pms_sku_info SkuInfoEntity info; // 2.sku的图片信息pms_sku_images List images; // 3.获取spu中的销售属性的组合 List saleAttrs; // 4.获取SPU的介绍 SpuInfoDescEntity desc; // 5.获取SPU的规格参数 List baseAttrs; } ``` 销售属性的VO ```java @ToString @Data public class SkuItemSaleAttrVo { private Long attrId; private String attrName; private String attrValue; } ``` 属性组相关的VO ```java @ToString @Data public class SpuItemGroupAttrVo { private String groupName; private List baseAttrs; } ``` 其中的Attr是之前就创建的VO对象 ## 2.商品详细的数据查询 ### 2.1 sku的基本信息 我们可以直接查询获取 ```java // 1.sku的基本信息 pms_sku_info SkuInfoEntity skuInfoEntity = getById(skuId); ``` ### 2.2 sku的图片信息 图片信息也是直接获取 ```java List images = skuImagesService.getImagesBySkuId(skuId); ``` ### 2.3 销售属性   销售属性的信息是根据SPU找到所有的SKU下的对应的属性信息,首先写出对应的SQL ```sql ## 根据SPU编号找到所有的SKU编号 然后进而找到所有的销售属性信息 SELECT # psi.sku_id, pssav.attr_id, pssav.attr_name, GROUP_CONCAT( DISTINCT pssav.attr_value) FROM `pms_sku_info` psi LEFT JOIN `pms_sku_sale_attr_value` pssav ON psi.sku_id = pssav.sku_id WHERE psi.spu_id = 6 GROUP BY pssav.attr_id,pssav.attr_name ``` SQL对应的查询结果 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/6cd63592008d46658ec63942443c2261.png) 对应的Mapper映射 ```xml ``` 然后对应的Dao接口 ```java @Mapper public interface SkuSaleAttrValueDao extends BaseMapper { List getSkuSaleAttrValueBySpuId(@Param("spuId") Long spuId); } ``` 销售属性中的Service接口及实现 ```java public interface SkuSaleAttrValueService extends IService { PageUtils queryPage(Map params); List getSkuSaleAttrValueBySpuId(Long spuId); } ``` ```java @Override public List getSkuSaleAttrValueBySpuId(Long spuId) { List attrsVo = skuSaleAttrValueDao.getSkuSaleAttrValueBySpuId(spuId); return attrsVo; } ``` 然后就是对应的SkuService中的串联 ```java // 3.获取spu中的销售属性的组合 List saleAttrs = skuSaleAttrValueService.getSkuSaleAttrValueBySpuId(spuId); ``` ### 2.4 spu的详情 也是直接获取的 ```java // 4.获取SPU的介绍 SpuInfoDescEntity spuInfoDescEntity = spuInfoDescService.getById(spuId); ``` ### 2.5 Spu的规格参数   Spu的规格参数,我们需要根据sku对应的商品分类和spu编号来查询,完整的SQL语句 ```sql # `pms_attr_group` # 根据 SPU编号和CatalogId类别编号 要查询出所有的属性组及其属性信息 SELECT t1.attr_group_id ,t1.attr_group_name ,t2.attr_id ,t3.attr_name ,t4.attr_value FROM `pms_attr_group` t1 LEFT JOIN `pms_attr_attrgroup_relation` t2 ON t1.attr_group_id = t2.attr_group_id LEFT JOIN `pms_attr` t3 ON t2.attr_id = t3.attr_id LEFT JOIN `pms_product_attr_value` t4 ON t4.attr_id = t2.attr_id WHERE t1.catelog_id = '225' AND t4.spu_id = 6 ``` 然后对应的查询效果 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/2e6174f065184c07971ee0a8e609e6cb.png) 具体的实现,Dao层接口和映射文件 ```java @Mapper public interface AttrGroupDao extends BaseMapper { List getAttrgroupWithSpuId(@Param("spuId") Long spuId, @Param("catalogId") Long catalogId); } ``` ```xml ``` 对应的AttrGroupService中的定义和实现 ```java List getAttrgroupWithSpuId(Long spuId , Long catalogId); ``` ```java /** * 跟进SpuId和CatalogId查询出对应的 属性组及其属性信息 * @param spuId * @param catalogId * @return */ @Override public List getAttrgroupWithSpuId(Long spuId, Long catalogId) { // List groupAttrVo = attrGroupDao.getAttrgroupWithSpuId(spuId,catalogId); return groupAttrVo; } ``` 然后就是对应的SkuInfoServiceImpl中的处理 ```java // 5.获取SPU的规格参数 List groupAttrVo = attrGroupService.getAttrgroupWithSpuId(spuId,catalogId); ``` ### 2.6 完成的处理   在SkuInfoServiceImpl中的完整的处理 ```java @Override public SpuItemVO item(Long skuId) { SpuItemVO vo = new SpuItemVO(); // 1.sku的基本信息 pms_sku_info SkuInfoEntity skuInfoEntity = getById(skuId); vo.setInfo(skuInfoEntity); // 获取对应的SPUID Long spuId = skuInfoEntity.getSpuId(); // 获取对应的CatalogId 类别编号 Long catalogId = skuInfoEntity.getCatalogId(); // 2.sku的图片信息pms_sku_images List images = skuImagesService.getImagesBySkuId(skuId); vo.setImages(images); // 3.获取spu中的销售属性的组合 List saleAttrs = skuSaleAttrValueService.getSkuSaleAttrValueBySpuId(spuId); vo.setSaleAttrs(saleAttrs); // 4.获取SPU的介绍 SpuInfoDescEntity spuInfoDescEntity = spuInfoDescService.getById(spuId); vo.setDesc(spuInfoDescEntity); // 5.获取SPU的规格参数 List groupAttrVo = attrGroupService.getAttrgroupWithSpuId(spuId,catalogId); vo.setBaseAttrs(groupAttrVo); return vo; } ``` 然后Controller中完成商品详情的数据查询,绑定数据后调整到商品详情页。之后做页面的渲染操作 ```java /** * 根据前端传递的SkuId我们需要查询出对有的商品信息 * @param skuId * @return */ @GetMapping("/{skuId}.html") public String item(@PathVariable Long skuId, Model model){ SpuItemVO itemVO = skuInfoService.item(skuId); model.addAttribute("item",itemVO); return "item"; } ``` ## 3.商品详情页渲染 ### 3.1 SKU标题 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/34c58cefba9d49b9a6d6063d358baf92.png) ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/94012d961b804b9ba1a1ea4461a75e2d.png) ### 3.2 图片展示 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/b302a4b4edc449bb9c83e76b70fe3704.png) ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/46426b256df042b48a642e2075e47b64.png) ### 3.3 价格和有货 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/69cb9f0af901422296b71d8e7d6f86cb.png) ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/2f2573f515cd43b3ac06cc5e87da4067.png) ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/eeea132fbacc417593f2d64e063671a5.png) ### 3.4 销售属性   销售属性是当前SKU对应的SPU下所有的销售属性的组合。 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/50a00b3c7266486f83bdea51a336c7fb.png) ### 3.5 SPU详情 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/62171fb18de1461c9b8295b82e4e9923.png) ### 3.6 规格参数 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/f1510d19cb634f5e9b95b93db125984a.png) ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/630ec28f2b094d2d9a691b14207f28cf.png) ## 4.异步处理   然后我们就可以在商品详细信息查询的位置实现CompletableFuture的异步编排处理。 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/20b058ecebc44c3da3a0c223512ec12c.png) 先定义线程池 ```java @Configuration public class MyThreadPoolConfig { @Bean public ThreadPoolExecutor threadPoolExecutor() { return new ThreadPoolExecutor(20 ,200 ,10 , TimeUnit.SECONDS ,new LinkedBlockingQueue(10000) , Executors.defaultThreadFactory() ,new ThreadPoolExecutor.AbortPolicy() ); } } ``` 具体的编排处理 ```java ```