谷粒商城笔记+踩坑(15)——商品详情搭建+异步编排

目录

1、搭建页面环境

1.1、配置 Nginx 和 网关

1.2、动静资源配置

1.3、搜索页到详情页跳转

2、模型类抽取和controller

2.1、分析首页需要展示的信息

2.2、首页模型类vo

2.3、销售属性组合

2.4、规格参数 

2.5、创建ItemController,展示当前sku的详情

3.0、业务流程,根据sku_id获取首页信息

3.1、sku 基本信息获取

3.2、获取sku的图片信息

3.3、获取spu的销售属性组合

3.3.1、SkuInfoServiceImpl 

3.3.2、SkuSaleAttrValueServiceImpl

3.3.3、dao

3.3.4、sql,查询指定spu_id下的所有销售属性id,name,value

3.3.5、mapper 

3.4、获取 spu 的介绍

3.5、获取 spu 的规格参数信息

4、前端,详情页渲染

5、sku组合切换,点击销售属性跳转sku商品

5.1、封装Vo类

5.2、mapper,通过sku_id获取销售属性

5.3、 前端,修改item.html文件,重新渲染销售属性

6、异步编排优化

6.1、环境准备 

6.1.1、添加线程池属性类 

6.1.2、导入依赖,spring元数据处理器

6.1.3、yml配置线程池

6.1.4、自定义线程池配置类

6.2、异步编排优化详情页查询业务


1、搭建页面环境

1.1、配置 Nginx 和 网关

配置 Nginx 和 网关

修改本地的 hosts文件 vim /etc/hosts

# Gulimall Host Start
127.0.0.1 gulimall.cn
127.0.0.1 search.gulimall.cn
127.0.0.1 item.gulimall.cn
# Gulimall Host End

配置Nginx(将search下的请求转给网关)
商城业务-检索服务的时候已经配置过了,这里不需要修改

 不确定可以再查看一下:

vim /mydata/nginx/conf/conf.d/gulimall.conf
server_name gulimall.com *.gulimall.com

image-20211125221554742

配置网关
修改gulimall-gateway服务 /src/main/resources路径下的 application.yml

- id: gulimall_host
  uri: lb://gulimall-product
  predicates:
    - Host=gulimall.com,item.gulimall.com    #设置也可以通过“item.gulimall.com”路由到商品模块

1.2、动静资源配置

 动态资源:

复制shangpinxiangqing.html到gulimall-product/src/main/resources/templates/目录下,并改名为item.index。

image-20211125222702368

修改“item.index”文件里的srcherf的静态资源地址,前缀加“/static/item”

谷粒商城笔记+踩坑(15)——商品详情搭建+异步编排_第1张图片

静态资源:

将静态资源上传至nginx

image-20211125223131908

1.3、搜索页到详情页跳转

  1. 修改gulimall-search服务中 list.html 文件

  2. 编写 Controller 实现页面跳转

  3. 添加“com.atguigu.gulimall.product.web.ItemController”类,代码如下:

    @Controller
    public class ItemController {
    
        /**
         * 展示当前sku的详情
         * @param skuId
         * @return
         */
        @GetMapping("/{skuId}.html")
        public String skuItem(@PathVariable("skuId") Long skuId) {
    
            System.out.println("准备查询:" + skuId + "的详情");
    
            return "item.html";    //返回到item.html
        }
    }
    
  4. 访问测试:
     

     搜索后点击商品进入详情页:

    谷粒商城笔记+踩坑(15)——商品详情搭建+异步编排_第2张图片

    谷粒商城笔记+踩坑(15)——商品详情搭建+异步编排_第3张图片


     

2、模型类抽取和controller

2.1、分析首页需要展示的信息

根据首页预期展示信息抽取:

注意:要分清哪些信息是spu的,哪些信息是sku的。 

2.2、首页模型类vo

package com.xx.gulimall.product.vo;

@ToString
@Data
public class SkuItemVo {

    //1、sku基本信息的获取  pms_sku_info
    private SkuInfoEntity info;

    private boolean hasStock = true;

    //2、sku的图片信息    pms_sku_images
    private List images;

    //3、获取spu的销售属性组合
    private List saleAttr;

    //4、获取spu的介绍
    private SpuInfoDescEntity desc;

    //5、获取spu的规格参数信息
    private List groupAttrs;

    //6、秒杀商品的优惠信息
    private SeckillSkuVo seckillSkuVo;

}

2.3、销售属性组合

@Data
public class SkuItemSaleAttrsVo {
    private Long attrId;
    private String attrName;
    private String attrValues;
}

2.4、规格参数 

@ToString
@Data
public class SpuItemAttrGroupVo {
    private String groupName;
    private List attrs;
}
@Data
public class Attr {

    private Long attrId;
    private String attrName;
    private String attrValue;

}

2.5、创建ItemController,展示当前sku的详情

package com.xxx.gulimall.product.web;
@Controller
public class ItemController {

    @Resource
    private SkuInfoService skuInfoService;

    /**
     * 展示当前sku的详情
     */
    @GetMapping("/{skuId}.html")
    public String skuItem(@PathVariable("skuId") Long skuId, Model model) throws ExecutionException, InterruptedException {

        System.out.println("准备查询" + skuId + "详情");

        SkuItemVo vos = skuInfoService.item(skuId);
        
        model.addAttribute("item",vos);

        return "item";
    }
}

3、业务实现(不使用异步)

3.0、业务流程,根据sku_id获取首页信息

  • 1、sku基本信息的获取  pms_sku_info
  • 2、sku的图片信息    pms_sku_images
  • 3、获取spu的销售属性组合
  • 4、获取spu的介绍    pms_spu_info_desc
  • 5、获取spu的规格参数信息

3.1、sku 基本信息获取

查询 pms_sku_info

com.xxx.gulimall.product.service.impl.SkuInfoServiceImpl
    @Override
    public SkuItemVo item(Long skuId) throws ExecutionException, InterruptedException{
        // 1、sku基本信息    pms_sku_info
        SkuInfoEntity info = getById(skuId);
        Long spuId=info.getSpuId();
        skuItemVo.setInfo(info);

    }

3.2、获取sku的图片信息

表pms_sku_images 

com.xxx.gulimall.product.service.impl.SkuInfoServiceImpl 

// 2、sku的图片信息   pms_sku_images
List images = skuImagesService.getImagesBySkuId(skuId);
skuItemVo.setImages(images);

注入 SkuImagesService,调用该实现类的 getImagesBySkuId(skuId) 方法获取spu的图片信息

com.atguigu.gulimall.product.service.impl 路径下的 SkuImagesServiceImpl 实现类编写:

@Override
public List getImagesBySkuId(Long skuId) {
    SkuImagesDao imagesDao = this.baseMapper;

    List imagesEntities = imagesDao.selectList(new QueryWrapper().eq("sku_id", skuId));
    return imagesEntities;
}

3.3、获取spu的销售属性组合

3.3.1、SkuInfoServiceImpl 

com.xxx.gulimall.product.service.impl.SkuInfoServiceImpl

// 3、获取 spu 的销售属性组合
List saleAttrVos = skuSaleAttrValueService.getSaleAttrsBySpuId(spuId);
skuItemVo.setSaleAttr(saleAttrVos);

3.3.2、SkuSaleAttrValueServiceImpl

@Override
public List getSaleAttrsBySpuId(Long spuId) {
    SkuSaleAttrValueDao dao = this.baseMapper;
    List saleAttrVos = dao.getSaleAttrsBySpuId(spuId);
    return saleAttrVos;
}

3.3.3、dao

使用SkuSaleAttrValueDao 层 getSaleAttrsBySpuId 方法:

package com.atguigu.gulimall.product.dao;

@Mapper
public interface SkuSaleAttrValueDao extends BaseMapper {

    List getSaleAttrsBySpuId(@Param("spuId") Long spuId);

}

3.3.4、sql,查询指定spu_id下的所有销售属性id,name,value

sku表pms_sku_info:

sku销售属性表pms_sku_sale_attr_value:

谷粒商城笔记+踩坑(15)——商品详情搭建+异步编排_第4张图片

左外连接查询:

SELECT    #查销售属性的id,name,值
	ssav.attr_id attr_id,
	ssav.attr_name attr_name,
	GROUP_CONCAT( DISTINCT ssav.attr_value ) attr_values 
FROM    #sku表左外连接sku销售属性表
	pms_sku_info info
	LEFT JOIN pms_sku_sale_attr_value ssav ON ssav.sku_id = info.sku_id 
WHERE
	spu_id = #{spuId}
	
GROUP BY
	ssav.attr_id,
	ssav.attr_name;

查询结果:

3.3.5、mapper 

gulimall-product/src/main/resources/mapper/product/SkuSaleAttrValueDao.xml


分析当前spu有多少了sku,所有sku涉及到的属性组合

  1. 通过spu_id 查询 pms_sku_info 表,获得当前spu对应的 sku_id
  2. 通过sku_id 查询 pms_sku_sale_attr_value表,获取 当前spu 对应的所有的sku的销售属性
  3. 通过汇总函数封装成我们想要的样子

3.4、获取 spu 的介绍

查询 pms_spu_info_desc

@Autowired
SpuInfoDescService spuInfoDescService;

// 4、获取 spu 的介绍 pms_spu_info_desc
Long spuId = info.getSpuId();
SpuInfoDescEntity spuInfoDescEntity = spuInfoDescService.getById(spuId);
skuItemVo.setDesp(spuInfoDescEntity);

3.5、获取 spu 的规格参数信息

查询 pms_spu_info_desc

@Autowired
AttrGroupService attrGroupService;

Long spuId = info.getSpuId();
Long catalogId = info.getCatalogId();
// 5、获取 spu 的规格参数信息 pms_spu_info_desc
List attrGroupVos = attrGroupService.getAttrGroupWithAttrsBySpuId(spuId,catalogId);
skuItemVo.setGroupAttrs(attrGroupVos);

注入 AttrGroupService,调用该实现类的 getAttrGroupWithAttrsBySpuId(spuId,catalogId) 方法

/**
 * 查处当前spuId对应的所有属性分组信息 以及 当前分组下的所有属性对应的值
 * @param spuId
 * @param catalogId
 * @return
 */
@Override
public List getAttrGroupWithAttrsBySpuId(Long spuId, Long catalogId) {
    AttrGroupDao baseMapper = this.getBaseMapper();
    List vos = baseMapper.getAttrGroupWithAttrsBySpuId(spuId,catalogId);
    return vos;
}

使用AttrGroupDao 层 getAttrGroupWithAttrsBySpuId 方法:

package com.atguigu.gulimall.product.dao;
@Mapper
public interface AttrGroupDao extends BaseMapper {

    List getAttrGroupWithAttrsBySpuId(@Param("spuId") Long spuId, @Param("catalogId") Long catalogId);
}

gulimall-product/src/main/resources/mapper/product/AttrGroupDao.xml :



    
    
        
        
    


这里使用了联表查询:

  1. 通过 catelog_id 查询 pms_attr_group 表中对应的 属性分组的信息 attr_group_idattr_group_name
  2. 通过 attr_group_id 联表查询 pms_attr_attrgroup_relation 表中的属性id attr_id
  3. 通过 attr_id 联表查询 pms_attr 表中对应的 attr_nameattr_id
  4. 通过 attr_id 联表查询 pms_product_attr_value 表中对应的 属性值 attr_value


 

4、前端,详情页渲染

1、添加thymeleaf的名称空间



2、标题名设置

华为 HUAWEI Mate 10 6GB+128GB 亮黑色 移动联通电信4G手机 双卡双待
预订用户预计11月30日左右陆续发货!麒麟970芯片!AI智能拍照!

3、大图显示

4、价格设置

5、是否有货

  • 无货, 此商品暂时售完
  • 6、小图显示

    7、销售属性

    选择[[${attr.attrName}]]
    [[${val}]]

    8、商品介绍

    
    

    9、规格包装

  • 主体

    品牌
    华为(HUAWEI)

    包装清单

    手机(含内置电池) X 1、5A大电流华为SuperCharge充电器X 1、5A USB数据线 X 1、半入耳式线控耳机 X 1、快速指南X 1、三包凭证 X 1、取卡针 X 1、保护壳 X 1


  •  

    5、sku组合切换,点击销售属性跳转sku商品

    需求:通过不同的销售属性渲染sku商品

    通过选择销售属性获取该销售属性对应的sku,通过算法选中该sku

    5.1、封装Vo类

    1. com.atguigu.gulimall.product.vo 路径下创建 AttrValueWithSkuIdVo 类

      @Data
      public class AttrValueWithSkuIdVo {
          private String attrValue;
          private String skuIds;
      }
      
    2. 修改 SkuItemSaleAttrsVo 类

      @Data
      public class SkuItemSaleAttrsVo {
          private Long attrId;
          private String attrName;
          private List attrValues;
      }
      

    5.2、mapper,通过sku_id获取销售属性

    修改gulimall-product/src/main/resources/mapper/product/SkuSaleAttrValueDao.xml

    
      
      
      
        
        
      
    
    
    

    5.3、 前端,修改item.html文件,重新渲染销售属性

    选择[[${attr.attrName}]]
    [[${vals.attrValue}]]
    
    


     

    6、异步编排优化

    6.1、环境准备 

    6.1.1、添加线程池属性类 

    添加线程池属性配置类,并注入到容器中

    package com.atguigu.gulimall.product.config;
    //跟gulimall.thread相关的配置文件绑定
    //这个注解设置yml配置文件前缀,这样配置后yml数据就会自动注入到 Bean 中,不用再@Value
    @ConfigurationProperties(prefix = "gulimall.thread")   
    @Component
    @Data
    public class ThreadPoolConfigProperties {
        private Integer coreSize;
        private Integer maxSize;
        private Integer keepAliveTime;
    }
    

    6.1.2、导入依赖,spring元数据处理器

    作用是给yml配置加提示,不导入也行。

    
        org.springframework.boot
        spring-boot-configuration-processor
        true
    
    

    6.1.3、yml配置线程池

    在gulimall-product服务中加入以下配置:

    # 配置线程池
    gulimall:
      thread:
        core-size: 20
        max-size: 200
        keep-alive-time: 10
    

    这是上面自定义的配置,因为导入了spring-boot-configuration-processor依赖,所以编写时有提示:

    谷粒商城笔记+踩坑(15)——商品详情搭建+异步编排_第5张图片

    6.1.4、自定义线程池配置类

    获取线程池的属性值这里直接调用与配置文件相对应的属性配置类

    package com.atguigu.gulimall.product.config;
    
    @Configuration
    public class MyThreadConfig {
    
        @Bean
        public ThreadPoolExecutor threadPoolExecutor(ThreadPoolConfigProperties pool) {
            return new ThreadPoolExecutor(pool.getCoreSize(),
                    pool.getMaxSize(),
                    pool.getKeepAliveTime(),
                    TimeUnit.SECONDS,
                    new LinkedBlockingDeque<>(100000),
                    Executors.defaultThreadFactory(),
                    new ThreadPoolExecutor.AbortPolicy());
        }
    }
    

    6.2、异步编排优化详情页查询业务

    注入线程池: 

    @Autowired
    ThreadPoolExecutor executor;
    
    • infoFuture

      • saleAttrFuture
      • descFuture
      • baseAttrFuture (这三个异步任务需要 infoFuture 执行完得到其结果才能执行)
    • imageFuture

    supplyAsync而不是runAsync ,以便于获取线程返回结果

    com.xxx.gulimall.product.service.impl.SkuInfoServiceImpl

    @Override
    public SkuItemVo item(Long skuId) {
        SkuItemVo skuItemVo = new SkuItemVo();
    
        // 1、sku基本信息    pms_sku_info
    //创建异步对象用supplyAsync而不是runAsync ,以便于获取线程返回结果。
        CompletableFuture infoFuture = CompletableFuture.supplyAsync(() -> {
            SkuInfoEntity info = getById(skuId);
            skuItemVo.setInfo(info);
            return info;
        }, executor);
    
        // 2、获取 spu 的销售属性组合
    //线程串行化用thenAcceptAsync接收第一步的结果即sku实体类,自己执行完没有返回结果
        CompletableFuture saleAttrFuture = infoFuture.thenAcceptAsync(res -> {
            List saleAttrVos = saleAttrValueService.getSaleAttrsBySpuId(res.getSpuId());
            skuItemVo.setSaleAttr(saleAttrVos);
        }, executor);
    
        // 3、获取 spu 的介绍 pms_spu_info_desc
    //这里也需要第一步的sku实体类,所以还是第一步future的thenAcceptAsync
        CompletableFuture descFuture = infoFuture.thenAcceptAsync(res -> {
            SpuInfoDescEntity spuInfoDescEntity = spuInfoDescService.getById(res.getSpuId());
            skuItemVo.setDesp(spuInfoDescEntity);
        }, executor);
    
        // 4、获取 spu 的规格参数信息 pms_spu_info_desc
        CompletableFuture baseAttrFuture = infoFuture.thenAcceptAsync(res -> {
            List attrGroupVos = attrGroupService.getAttrGroupWithAttrsBySpuId(res.getSpuId(), res.getCatalogId());
            skuItemVo.setGroupAttrs(attrGroupVos);
        }, executor);
    
        // 5、sku的图片信息   pms_sku_images
    //这个任务跟前面几个任务都没关系
    //这里创建异步对象用runAsync 而不是supplyAsync,因为不需要获取线程结果
        CompletableFuture imageFuture = CompletableFuture.runAsync(() -> {
            List images = imagesService.getImagesBySkuId(skuId);
            skuItemVo.setImages(images);
        }, executor);
    
    
        // 等待所有任务都完成
    //多任务组合,allOf等待所有任务完成。这里就不需要加infoFuture,因为依赖于它结果的saleAttrFuture等都完成了,它肯定也完成了。
        CompletableFuture.allOf(saleAttrFuture,descFuture,baseAttrFuture,imageFuture).get();
    
        return skuItemVo;
    }
    

    你可能感兴趣的:(java学习路线,谷粒商城项目,springboot,SpringCloud,nginx)