6.商城业务-异步编排优化

60 阅读3分钟

1.环境准备

  • 配置本地域名解析hosts文件

    192.168.101.1 item.mall.com
    
  • nginx配置文件中配置的是*.mall.com,故不用再修改了,可以实现监听。

  • 修改网关配置,跳转到商品页

    - id: mall_host_route
              uri: lb://mall-product
              predicates:
                - Host=www.mall.com,item.mall.com
    
  • 将前端静态页面文件放在product服务下,并修改为item.html

    image-20230124190149450.png

  • 将静态资源防止nginx对应item【自行创建】目录下

  • 将search服务中详细商品服务跳转链接配置上

    image-20230124190715916.png

  • 将item.html页面中获取静态资源路径进行修改

2.编写商城业务

1)Controller编写

  • 需要获取页面跳转后的skuId,显示对应skuId的sku详细信息
@Controller
public class ItemController {
    @Autowired
    SkuInfoService skuInfoService;
    /**
     * 展示当前sku的详情
     * @return
     */
    @GetMapping("/{skuId}.html")
    public String skuItem(@PathVariable("skuId") Long skuId, Model model){
        SkuItemVo item = skuInfoService.item(skuId);
        model.addAttribute("item", item);
        return "item";
    }
}

2)具体实现业务

  • 将需要获取的数据封装成一个VO,方便封装
  • 需要五部分信息
    1. 指定skuId的sku基本信息【SkuInfoEntity】
    2. 指定skuId的sku多个图片信息【SkuImagesEntity】
    3. 指定skuId对应的spu下所有sku的属性信息【SkuItemSaleAttrVo】
    4. 指定skuId对应的spu详细介绍信息【SpuInfoDescEntity】
    5. 指定skuId对应的spu规格参数信息【SpuItemAttrGroupVo】
@Data
public class SkuItemVo {
    //1、sku基本信息的获取  pms_sku_info
    private SkuInfoEntity info;

    private boolean hasStock = true;

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

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

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

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

}
@Data
@ToString
public class SkuItemSaleAttrVo {

    private Long attrId;

    private String attrName;

    private List<AttrValueWithSkuIdVo> attrValues;

}
@Data
@ToString
public class SpuItemAttrGroupVo {

    private String groupName;

    private List<Attr> attrs;

}

详细业务代码编写

public SkuItemVo item(Long skuId) {
    SkuItemVo skuItemVo = new SkuItemVo();
    // 1.sku基本信息获取
    SkuInfoEntity info = getById(skuId);
    Long spuId = info.getSpuId();
    skuItemVo.setInfo(info);
    // 2.sku的图片信息
    List<SkuImagesEntity> images = skuImagesService.getImagesBySkuId(skuId);
    skuItemVo.setImages(images);
    // 3.获取spu的销售属性组合
    List<SkuItemSaleAttrVo> saleAttrVos = skuSaleAttrValueService.getSaleAttrsBySpuId(spuId);
    skuItemVo.setSaleAttr(saleAttrVos);
    // 4.获取spu的介绍
    SpuInfoDescEntity spuInfoDescEntity = spuInfoDescService.getById(spuId);
    skuItemVo.setDesc(spuInfoDescEntity);
    // 5.获取spu的规格参数信息
    Long catalogId = info.getCatalogId();
    List<SpuItemAttrGroupVo> attrGroupVos = attrGroupService.getAttrGroupWithAttrBySpuId(spuId, catalogId);
    skuItemVo.setGroupAttrs(attrGroupVos);

    return skuItemVo;
}

获取图片信息

@Override
public List<SkuImagesEntity> getImagesBySkuId(Long skuId) {
    List<SkuImagesEntity> images = this.baseMapper.selectList(new QueryWrapper<SkuImagesEntity>().eq("sku_id", skuId));
    return images;
}

获取销售属性信息

@Override
public List<SkuItemSaleAttrVo> getSaleAttrsBySpuId(Long spuId) {
    List<SkuItemSaleAttrVo> vos = this.baseMapper.getSaleAttrsBySpuId(spuId);
    return vos;
}
  • 对应SkuSaleAttrValueDao.xml
<select id="getSaleAttrsBySpuId" resultType="com.white.mall.product.vo.SkuItemSaleAttrVo">
    select
    ssav.attr_id attr_id,
    ssav.attr_name attr_name,
    GROUP_CONCAT(DISTINCT ssav.attr_value) attr_values
    from pms_sku_info info
    left join pms_sku_sale_attr_value ssav on ssav.sku_id = info.sku_id
    where info.spu_id = #{spuId}
    group by ssav.attr_id , ssav.attr_name
</select>

获取规格参数信息

@Override
public List<SpuItemAttrGroupVo> getAttrGroupWithAttrBySpuId(Long spuId, Long catalogId) {
    List<SpuItemAttrGroupVo> vos = this.baseMapper.getAttrGroupWithAttrBySpuId(spuId, catalogId);
    return vos;
}
  • 对应AttrGroupDao.xml
<resultMap id="spuItemAttrGroupVo" type="com.white.mall.product.vo.SpuItemAttrGroupVo">
    <result property="groupName" column="attr_group_name"></result>
    <collection property="attrs" ofType="com.white.mall.product.vo.Attr">
        <result property="attrName" column="attr_name"></result>
        <result property="attrValue" column="attr_value"></result>
    </collection>
</resultMap>

<select id="getAttrGroupWithAttrBySpuId"
        resultMap="spuItemAttrGroupVo">
    SELECT value.spu_id,
        group.attr_group_name,
        group.attr_group_id,
        attr.attr_name,
        value.attr_value
    FROM pms_attr_group `group`
    LEFT JOIN pms_attr_attrgroup_relation `rela` ON group.attr_group_id = rela.attr_group_id
    LEFT JOIN pms_attr `attr` ON rela.attr_id = attr.attr_id
    LEFT JOIN pms_product_attr_value `value` ON value.attr_id = attr.attr_id
    WHERE group.catelog_id = #{catalogId} AND value.spu_id = #{spuId}
</select>

3)点击规格实现sku跳转

后端逻辑

  • 编写sql获取到所涉及到的规格对应的skuIds

  • 跳转页面时,前端获取到所有规格的交集skuId,完成页面跳转

    <resultMap id="skuItemSaleAttrVo" type="com.white.mall.product.vo.SkuItemSaleAttrVo">
        <result column="attr_id" property="attrId"></result>
        <result column="attr_name" property="attrName"></result>
        <collection property="attrValues" ofType="com.white.mall.product.vo.AttrValueWithSkuIdVo">
            <result column="sku_ids" property="skuIds"></result>
            <result column="attr_values" property="attrValue"></result>
        </collection>
    </resultMap>
    
    <select id="getSaleAttrsBySpuId" resultMap="skuItemSaleAttrVo">
        SELECT
                attr.attr_id attr_id,
                attr.attr_name attr_name,
                attr.attr_value attr_values,
                GROUP_CONCAT(DISTINCT info.sku_id) sku_ids
        FROM pms_sku_info `info`
        LEFT JOIN pms_sku_sale_attr_value `attr` ON info.sku_id = attr.sku_id
        WHERE info.spu_id = #{spuId}
        GROUP BY attr.attr_id, attr.attr_name,attr.attr_value
    </select>
    

    image-20230124200956992.png

前端逻辑

  • 根据skuIds集合, 修改对应sku的属性样式并将其边框标红
  • 找到多个属性集合中公共的那个skuId【foreach遍历】
  • 实现页面跳转

4)异步编排

配置线程池

@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()
        );
    }
}
@Component
@ConfigurationProperties(prefix = "mall.thread")
@Data
public class ThreadPoolConfigProperties {
    private Integer coreSize;
    private Integer MaxSize;
    private Integer keepAliveTime;
}

添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

配置编写

mall.thread.core-size=20
mall.thread.max-size=200
mall.thread.keep-alive-time=10

代码编写

  • 除了图片信息,其它信息需要拿到sku基本信息才能继续执行
  • 图片信息与sku基本信息无关,因此可以单独开启一个异步任务
@Override
public SkuItemVo item(Long skuId) throws ExecutionException, InterruptedException {
    SkuItemVo skuItemVo = new SkuItemVo();

    CompletableFuture<SkuInfoEntity> infoFuture = CompletableFuture.supplyAsync(() -> {
        // 1.sku基本信息获取
        SkuInfoEntity info = getById(skuId);
        skuItemVo.setInfo(info);
        return info;
    }, executor);


    CompletableFuture<Void> saleAttrFuture = infoFuture.thenAcceptAsync((res) -> {
        // 2.获取spu的销售属性组合
        List<SkuItemSaleAttrVo> saleAttrVos = skuSaleAttrValueService.getSaleAttrsBySpuId(res.getSpuId());
        skuItemVo.setSaleAttr(saleAttrVos);
    }, executor);



    CompletableFuture<Void> descFuture = infoFuture.thenAcceptAsync((res) -> {
        // 3.获取spu的介绍
        SpuInfoDescEntity spuInfoDescEntity = spuInfoDescService.getById(res.getSpuId());
        skuItemVo.setDesc(spuInfoDescEntity);
    }, executor);


    CompletableFuture<Void> baseAttrFuture = infoFuture.thenAcceptAsync((res) -> {
        // 4.获取spu的规格参数信息
        List<SpuItemAttrGroupVo> attrGroupVos = attrGroupService.getAttrGroupWithAttrBySpuId(res.getSpuId(), res.getCatalogId());
        skuItemVo.setGroupAttrs(attrGroupVos);
    }, executor);



    // 5.sku的图片信息,与infoFuture无关,因此可以单独开一个异步任务
    CompletableFuture<Void> imageFuture = CompletableFuture.runAsync(() -> {
        List<SkuImagesEntity> images = skuImagesService.getImagesBySkuId(skuId);
        skuItemVo.setImages(images);
    }, executor);

    //等到所有任务都完成
    CompletableFuture.allOf(saleAttrFuture,descFuture,baseAttrFuture,imageFuture).get();

    return skuItemVo;
}@Override
public SkuItemVo item(Long skuId) throws ExecutionException, InterruptedException {
    SkuItemVo skuItemVo = new SkuItemVo();

    CompletableFuture<SkuInfoEntity> infoFuture = CompletableFuture.supplyAsync(() -> {
        // 1.sku基本信息获取
        SkuInfoEntity info = getById(skuId);
        skuItemVo.setInfo(info);
        return info;
    }, executor);


    CompletableFuture<Void> saleAttrFuture = infoFuture.thenAcceptAsync((res) -> {
        // 2.获取spu的销售属性组合
        List<SkuItemSaleAttrVo> saleAttrVos = skuSaleAttrValueService.getSaleAttrsBySpuId(res.getSpuId());
        skuItemVo.setSaleAttr(saleAttrVos);
    }, executor);



    CompletableFuture<Void> descFuture = infoFuture.thenAcceptAsync((res) -> {
        // 3.获取spu的介绍
        SpuInfoDescEntity spuInfoDescEntity = spuInfoDescService.getById(res.getSpuId());
        skuItemVo.setDesc(spuInfoDescEntity);
    }, executor);


    CompletableFuture<Void> baseAttrFuture = infoFuture.thenAcceptAsync((res) -> {
        // 4.获取spu的规格参数信息
        List<SpuItemAttrGroupVo> attrGroupVos = attrGroupService.getAttrGroupWithAttrBySpuId(res.getSpuId(), res.getCatalogId());
        skuItemVo.setGroupAttrs(attrGroupVos);
    }, executor);



    // 5.sku的图片信息,与infoFuture无关,因此可以单独开一个异步任务
    CompletableFuture<Void> imageFuture = CompletableFuture.runAsync(() -> {
        List<SkuImagesEntity> images = skuImagesService.getImagesBySkuId(skuId);
        skuItemVo.setImages(images);
    }, executor);

    //等到所有任务都完成
    CompletableFuture.allOf(saleAttrFuture,descFuture,baseAttrFuture,imageFuture).get();

    return skuItemVo;
}