• 谷粒商城10——搜索、商品详情、异步编排


    六、商城业务-搜索页面

    1.页面跳转配置

    [root@iZ2ze30iuo2wom5i3mm5qlZ /]# cd /mydata/nginx/conf/conf.d/
    [root@iZ2ze30iuo2wom5i3mm5qlZ conf.d]# vi gulimall.conf
    
    
    • 1
    • 2
    • 3

    以gulimall.com结尾的访问路径 都转到网关去

    网关配置:

    - id: admin_host_route
      uri: lb://gulimall-product
      predicates:
        - Host=gulimall.com
    
    - id: admin_search_route
      uri: lb://gulimall-search
      predicates:
        - Host=search.gulimall.com
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AV6IJ6eb-1659072942560)(C:\Users\10418\AppData\Roaming\Typora\typora-user-images\image-20220723110710007.png)]

    配置search服务页面跳转

    @Controller
    public class SearchController {
    
        @GetMapping("/list.html")
        public String listPage(){
            return "list";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2.封装、编写vo实体类

    封装前端检索条件:

    keyword=小米&sort=saleCount_desc/asc&hasStock=0/1&skuPrice=400_1900&brandId=1&catalog3Id=1&at trs=1_3G:4G:5G&attrs=2_骁龙845&attrs=4_高清屏

    @Data
    public class SearchParam {
        /**
         * 页面传递过来的全文匹配关键字
         */
        private String keyword;
    
        /**
         * 品牌id,可以多选
         */
        private List<Long> brandId;
    
        /**
         * 三级分类id
         */
        private Long catalog3Id;
    
        /**
         * 排序条件:sort=price/salecount/hotscore_desc/asc
         */
        private String sort;
    
        /**
         * 是否显示有货
         */
        private Integer hasStock = 1;
    
        /**
         * 价格区间查询
         */
        private String skuPrice;
    
        /**
         * 按照属性进行筛选
         */
        private List<String> attrs;
    
        /**
         * 页码
         */
        private Integer pageNum = 1;
    
        /**
         * 原生的所有查询条件
         */
        private String _queryString;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    封装返回数据:

    @Data
    public class SearchResponse {
    
        /**
         * 所有商品信息
         */
        private List<SkuModel> products;
    
        /**
         * 分页信息
         */
        private Integer pageNum;//当前页码
        private Long total;//总记录数
        private Long totalPage;//总页码
    
        /**
         * 当前查询到的结果,所以涉及到的品牌
         */
        private List<BrandVo> brands;
    
        /**
         *当前查询到的结果,所以涉及到的分类
         */
        private List<CatalogVo> catalogs;
    
        /**
         * 当前查询到的结果,所以涉及到的属性
         */
        private List<AttrVo> attrs;
    
        @Data
        public static class BrandVo{
            private Long brandId;
            private String brandName;
            private String brandImg;
        }
    
        @Data
        public static class CatalogVo{
            private Long catalogId;
            private String catalogName;
        }
    
        @Data
        public static class AttrVo{
            private Long attrId;
            private String attrName;
            private List<String> attrValue;
        }
    
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    3.ES 编写 DSL

    //数据迁移
    PUT gulimall_product
    {
      "mappings": {
        "properties": {
          "skuId": {
            "type": "long"
          },
          "spuId": {
            "type": "keyword"
          },
          "skuTitle": {
            "type": "text",
            "analyzer": "ik_smart"
          },
          "skuPrice": {
            "type": "keyword"
          },
          "skuImg": {
            "type": "keyword"
          },
          "saleCount": {
            "type": "long"
          },
          "hasStock": {
            "type": "boolean"
          },
          "hotScore": {
            "type": "long"
          },
          "brandId": {
            "type": "long"
          },
          "catelogId": {
            "type": "long"
          },
          "brandName": {
            "type": "keyword"
          },
          "brandImg": {
            "type": "keyword"
          },
          "catelogName": {
            "type": "keyword"
          },
          "attrs": {
            "type": "nested",
            "properties": {
              "attrId": {
                "type": "long"
              },
              "attrName": {
                "type": "keyword"
              },
              "attrValue": {
                "type": "keyword"
              }
            }
          }
        }
      }
    }
    
    
    GET gulimall_product/_mapping
    
    
    POST _reindex
    {
      "source": {
        "index": "product"
      },
      "dest": {
        "index":"gulimall_product"
      }
    }
    
    
    
    //查询条件
    GET gulimall_product/_search
    {
      "query": {
        "bool": {
          "must": [
            {
              "match": {
                "skuTitle": "华为"
              }
            }
          ],
          "filter": [
            {
              "term": {
                "catelogId": "225"
              }
            },
            {
              "terms": {
                "brandId": [
                  "9"
                ]
              }
            },
            {
              "term": {
                "hasStock": "true"
              }
            },
            {
              "range": {
                "skuPrice": {
                  "gte": 1000,
                  "lte": 7000
                }
              }
            },
            {
              "nested": {
                "path": "attrs",
                "query": {
                  "bool": {
                    "must": [
                      {
                        "term": {
                          "attrs.attrId": {
                            "value": "15"
                          }
                        }
                      }
                    ]
                  }
                }
              }
            }
          ]
        }
      },
      "sort": [
        {
          "skuPrice": {
            "order": "desc"
          }
        }
      ],
      "from": 0,
      "size": 5,
      "highlight": {
        "fields": {
          "skuTitle": {}
        },
        "pre_tags": "",
        "post_tags": ""
      },
      "aggs": {
        "brandAgg": {
          "terms": {
            "field": "brandId",
            "size": 10
          },
          "aggs": {
            "brandNameAgg": {
              "terms": {
                "field": "brandName",
                "size": 10
              }
            },
            "brandImgAgg": {
              "terms": {
                "field": "brandImg",
                "size": 10
              }
            }
          }
        },
        "catelogAgg": {
          "terms": {
            "field": "catelogId",
            "size": 10
          },
          "aggs": {
            "catelogNameAgg": {
              "terms": {
                "field": "catelogName",
                "size": 10
              }
            }
          }
        },
        "attrs": {
          "nested": {
            "path": "attrs"
          },
          "aggs": {
            "attrIdAgg": {
              "terms": {
                "field": "attrs.attrId",
                "size": 10
              },
              "aggs": {
                "attrNameAgg": {
                  "terms": {
                    "field": "attrs.attrName",
                    "size": 10
                  }
                }
              }
            }
          }
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212

    4.业务代码

    =================================SearchController.java==================================
    @Controller
    public class SearchController {
    
        @Autowired
        MallSearchService mallSearchService;
    
        @GetMapping("/list.html")
        public String listPage(SearchParam searchParam, Model model){
    
            SearchResult searchResult = mallSearchService.search(searchParam);
            model.addAttribute("result",searchResult);
            return "list";
        }
    }
    ====================================MallSearchServiceImpl.java================================
    //去es进行检索
    @Override
    public SearchResult search(SearchParam searchParam) {
        //1.动态构建出要查询的DSL语句
        SearchResult searchResult = null;
    
        //1.准备检索请求
        SearchRequest searchRequest = buildSearchRequest(searchParam);
    
        try {
            //2.执行检索请求
            SearchResponse response = client.search(searchRequest, GulimallElasticSearchConfig.COMMON_OPTIONS);
    
            //3.分析响应数据,封装成想要的格式
            searchResult = buildSearchResult(response);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return searchResult;
    }
    
    /**
     * 1.准备检索请求
     * @return
     * @param searchParam
     */
    private SearchRequest buildSearchRequest(SearchParam searchParam) {
    
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();//构建DSL语句
    
        /**
         * 查询:模糊匹配、过滤(按照属性、分类、品牌、价格区间、库存)
         */
        //1.构建bool query
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        //1.1 must -模糊匹配 keyword
        if (!StringUtils.isEmpty(searchParam.getKeyword())){
            //match
            boolQuery.must(QueryBuilders.matchQuery("skuTitle",searchParam.getKeyword()));
        }
        //1.2 bool-filter catelogId
        if (searchParam.getCatalog3Id() != null){
            boolQuery.filter(QueryBuilders.termQuery("catelogId",searchParam.getCatalog3Id()));
        }
        //1.2 bool-filter brandId
        if (searchParam.getBrandId() != null && searchParam.getBrandId().size() > 0){
            boolQuery.filter(QueryBuilders.termsQuery("brandId",searchParam.getBrandId()));
        }
        //1.2 bool-filter hasStock
        boolQuery.filter(QueryBuilders.termQuery("hasStock",searchParam.getHasStock() == 1));
        //1.2 bool-filter skuPrice  1_500/_500/500_
        if (!StringUtils.isEmpty(searchParam.getSkuPrice())){
            RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("skuPrice");
            String[] s = searchParam.getSkuPrice().split("_");
            if (s.length == 2){
                //rangeQuery.gte(s[0]).lte(s[1]);
                //"_6000".split("_") = {"","6000"}
                if (!s[0].isEmpty()){
                    rangeQuery.gte(s[0]);
                }
                rangeQuery.lte(s[1]);
            }else if (s.length == 1){
                if (searchParam.getSkuPrice().startsWith("_")){
                    rangeQuery.lte(s[0]);
                }
                if (searchParam.getSkuPrice().endsWith("_")){
                    rangeQuery.lte(s[0]);
                }
            }
            boolQuery.filter(rangeQuery);
        }
        //1.2 bool-filter attrs  {attrID,attrID,attrID...}
        if (searchParam.getAttrs() != null && searchParam.getAttrs().size() > 0){
            //attrs=1_5寸:8寸&attrs=2_16G:8G
            for (String attr : searchParam.getAttrs()) {
                BoolQueryBuilder nestedBoolQuery = QueryBuilders.boolQuery();
                //attrs=1_5寸:8寸
                String[] s = attr.split("_");//1 5寸:8寸
                String attrId = s[0];//1
                String[] attrValues = s[1].split(":");//5寸 8寸
                nestedBoolQuery.must(QueryBuilders.termQuery("attrs.attrId",attrId));
                nestedBoolQuery.must(QueryBuilders.termsQuery("attrs.attrValue",attrValues));
                //attrs中的每一个attr 都对应一个 nestedQueryBuilder
                NestedQueryBuilder nestedQuery = QueryBuilders.nestedQuery("attrs", nestedBoolQuery, ScoreMode.None);
                boolQuery.filter(nestedQuery);
            }
        }
        //封装所有的查询条件
        sourceBuilder.query(boolQuery);
    
        /**
         * 排序、分页、高亮
         */
        //2.1 排序 sort=hotScore_asc/desc
        if (!StringUtils.isEmpty(searchParam.getSort())){
            String[] s = searchParam.getSort().split("_");
            sourceBuilder.sort(s[0], SortOrder.valueOf(s[1]));
        }
        //2.2 分页
        sourceBuilder.from((searchParam.getPageNum()-1) * EsConstant.PRODUCT_PAGE_SIZE);
        sourceBuilder.size(EsConstant.PRODUCT_PAGE_SIZE);
    
        //2.3 高亮 根据关键字 匹配高亮
        if (!StringUtils.isEmpty(searchParam.getKeyword())){
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("skuTitle");
            highlightBuilder.preTags("");
            highlightBuilder.postTags("");
            sourceBuilder.highlighter(highlightBuilder);
        }
    
        /**
         * 聚合分析
         */
        //3.1 品牌聚合
        TermsAggregationBuilder brand_agg = AggregationBuilders.terms("brand_agg");
        brand_agg.field("brandId").size(50);
        //品牌聚合的子聚合
        brand_agg.subAggregation(AggregationBuilders.terms("brand_name_agg").field("brandName").size(1));
        brand_agg.subAggregation(AggregationBuilders.terms("brand_img_agg").field("brandImg").size(1));
        sourceBuilder.aggregation(brand_agg);
    
        //3.2 分类聚合
        TermsAggregationBuilder catelog_agg = AggregationBuilders.terms("catelog_agg");
        catelog_agg.field("catelogId").size(2);
        catelog_agg.subAggregation(AggregationBuilders.terms("catelog_name_agg").field("catelogName").size(1));
        sourceBuilder.aggregation(catelog_agg);
    
        //3.3 属性聚合
        NestedAggregationBuilder attr_agg = AggregationBuilders.nested("attr_agg", "attrs");
        //聚合出所有的 attr_id_agg
        TermsAggregationBuilder attr_id_agg = AggregationBuilders.terms("attr_id_agg").field("attrs.attrId");
        //聚合分析出当前 attr_id对应的名字
        attr_id_agg.subAggregation(AggregationBuilders.terms("attr_name_agg").field("attrs.attrName").size(1));
        //聚合分析出当前 attr_id对应的所有可能的属性值
        attr_id_agg.subAggregation(AggregationBuilders.terms("attr_value_agg").field("attrs.attrValue").size(50));
    
        attr_agg.subAggregation(attr_id_agg);
        sourceBuilder.aggregation(attr_agg);
    
    
        String s = sourceBuilder.toString();
        System.out.println(s);
    
        //索引 、 DSL语句
        SearchRequest searchRequest = new SearchRequest(new String[]{EsConstant.PRODUCT_INDEX}, sourceBuilder);
    
        return searchRequest;
    }
     /**
         * 3.分析响应数据,封装成想要的格式
         * @param response
         * @return
         */
        private SearchResult buildSearchResult(SearchResponse response,SearchParam searchParam) {
            SearchResult searchResult = new SearchResult();
            SearchHits hits = response.getHits();
            //1.返回所以查询到的商品
            List<SkuModel> esModels = new ArrayList<>();
            if (hits.getHits() != null && hits.getHits().length > 0){
                for (SearchHit hit : hits.getHits()) {
                    String sourceAsString = hit.getSourceAsString();
                    SkuModel esModel = JSON.parseObject(sourceAsString,SkuModel.class);
                    if (!StringUtils.isEmpty(searchParam.getKeyword())) {
                        HighlightField skuTitle = hit.getHighlightFields().get("skuTitle");
                        String highLightTitle = skuTitle.getFragments()[0].string();
                        esModel.setSkuTitle(highLightTitle);
                    }
                    esModels.add(esModel);
                }
            }
            searchResult.setProducts(esModels);
    
            //2.当前所有商品涉及到的属性信息
            ParsedNested attr_agg = response.getAggregations().get("attr_agg");
            List<SearchResult.AttrVo> attrVos = new ArrayList<>();
            ParsedLongTerms attr_id_agg = attr_agg.getAggregations().get("attr_id_agg");
            for (Terms.Bucket bucket : attr_id_agg.getBuckets()) {
                SearchResult.AttrVo attrVo = new SearchResult.AttrVo();
                //获取属性的id
                Long attrId = bucket.getKeyAsNumber().longValue();
                attrVo.setAttrId(attrId);
                //获取属性的name
                ParsedStringTerms attr_name_agg = bucket.getAggregations().get("attr_name_agg");
                String attrName = attr_name_agg.getBuckets().get(0).getKeyAsString();
                attrVo.setAttrName(attrName);
                //获取属性的value
                ParsedStringTerms attr_value_age = bucket.getAggregations().get("attr_value_agg");
                List<String> attrValue = attr_value_age.getBuckets().stream().map(item -> {
                    String keyAsString = item.getKeyAsString();
                    return keyAsString;
                }).collect(Collectors.toList());
                attrVo.setAttrValue(attrValue);
    
                attrVos.add(attrVo);
            }
            searchResult.setAttrs(attrVos);
    
            //3.当前所有商品涉及到的品牌信息
            ParsedLongTerms brand_agg = response.getAggregations().get("brand_agg");
            List<SearchResult.BrandVo> brandVos = new ArrayList<>();
            for (Terms.Bucket bucket : brand_agg.getBuckets()) {
                SearchResult.BrandVo brandVo = new SearchResult.BrandVo();
                //获取品牌id
                String keyAsString = bucket.getKeyAsString();
                brandVo.setBrandId(Long.parseLong(keyAsString));
                //获取品牌名
                ParsedStringTerms brand_name_agg = bucket.getAggregations().get("brand_name_agg");
                String brand_name = brand_name_agg.getBuckets().get(0).getKeyAsString();
                brandVo.setBrandName(brand_name);
                //获取品牌图片
                ParsedStringTerms brand_img_agg = bucket.getAggregations().get("brand_img_agg");
                String brand_img = brand_img_agg.getBuckets().get(0).getKeyAsString();
                brandVo.setBrandImg(brand_img);
    
                brandVos.add(brandVo);
            }
            searchResult.setBrands(brandVos);
            //4.当前所有商品涉及到的分类信息
            ParsedLongTerms catelog_agg = response.getAggregations().get("catelog_agg");
            List<SearchResult.CatalogVo> catalogVos = new ArrayList<>();
            for (Terms.Bucket bucket : catelog_agg.getBuckets()) {
                SearchResult.CatalogVo catalogVo = new SearchResult.CatalogVo();
                //得到分类id
                String keyAsString = bucket.getKeyAsString();
                catalogVo.setCatalogId(Long.parseLong(keyAsString));
    
                //得到分类名
                ParsedStringTerms catelog_name_agg = bucket.getAggregations().get("catelog_name_agg");
                String catelog_name = catelog_name_agg.getBuckets().get(0).getKeyAsString();
                catalogVo.setCatalogName(catelog_name);
    
                catalogVos.add(catalogVo);
            }
            searchResult.setCatalogs(catalogVos);
    
            //5.分页信息-页码
            searchResult.setPageNum(searchParam.getPageNum());
            //5.分页信息-总记录数
            long total = hits.getTotalHits().value;
            searchResult.setTotal(total);
            //5.分页信息-总页码
            int totoalPages = (int) total%EsConstant.PRODUCT_PAGE_SIZE == 0 ? (int) total/EsConstant.PRODUCT_PAGE_SIZE : (int) (total/EsConstant.PRODUCT_PAGE_SIZE + 1);
            searchResult.setTotalPage(totoalPages);
    
            return null;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263

    七、CompletableFuture-异步编排

    1.线程池回顾

    参考博客:《Java并发编程的艺术》2 第九章 Java中的线程池_HotRabbit.的博客-CSDN博客

    2.异步

    • Future 是 Java 5 添加的类,用来描述一个异步计算的结果。你可以使用isDone方法检查计 算是否完成,或者使用get阻塞住调用线程,直到计算完成返回结果,你也可以使用cancel 方法停止任务的执行。
    • 虽然Future以及相关使用方法提供了异步执行任务的能力,但是对于结果的获取却是很不 方便,只能通过阻塞或者轮询的方式得到任务的结果。阻塞的方式显然和我们的异步编程的 初衷相违背,轮询的方式又会耗费无谓的 CPU 资源,而且也不能及时地得到计算结果,为 什么不能用观察者设计模式当计算结果完成及时通知监听者呢?
    • 很多语言,比如 Node.js,采用回调的方式实现异步编程。Java 的一些框架,比如 Netty,自 己扩展了 Java 的 Future接口,提供了addListener等多个扩展方法;Google guava 也提供了 通用的扩展 Future;Scala 也提供了简单易用且功能强大的 Future/Promise 异步编程模式。
    • 在 Java 8 中, 新增加了一个包含 50 个方法左右的类: CompletableFuture,提供了非常强大的 Future 的扩展功能,可以帮助我们简化异步编程的复杂性,提供了函数式编程的能力,可以 通过回调的方式处理计算结果,并且提供了转换和组合 CompletableFuture 的方法。 CompletableFuture 类实现了 Future 接口,所以你还是可以像以前一样通过get方法阻塞或 者轮询的方式获得结果,但是这种方式不推荐使用。
    • CompletableFuture 和 FutureTask 同属于 Future 接口的实现类,都可以获取线程的执行结果。

    image-20220726170142885

    3.创建异步对象

    CompletableFuture 提供了四个静态方法来创建一个异步操作。

    image-20220726172201396

    image-20220726172748997

    get方法会阻塞当前线程,直到提交的任务执行完

    image-20220726173028477

    3.1 计算完成时回调方法

    image-20220726203449896

    whenComplete 可以处理正常和异常的计算结果,exceptionally 处理异常情况。

    whenComplete 和 whenCompleteAsync 的区别:

    • whenComplete:是执行当前任务的线程执行继续执行 whenComplete 的任务。
    • whenCompleteAsync:是执行把 whenCompleteAsync 这个任务继续提交给线程池 来进行执行

    方法不以 Async 结尾,意味着 Action 使用相同的线程执行,而 Async 可能会使用其他线程 执行(如果是使用相同的线程池,也可能会被同一个线程选中执行)

    image-20220726204338389

    3.2 handle方法

    和 complete 一样,可对结果做最后的处理(可处理异常),可改变返回值。

    image-20220726204912755

    3.3 线程串行化方法

    image-20220726205819559

    • thenApply 方法:当一个线程依赖另一个线程时,获取上一个任务返回的结果,并返回当前 任务的返回值。
    • thenAccept 方法:消费处理结果。接收任务的处理结果,并消费处理,无返回结果。
    • thenRun 方法:只要上面的任务执行完成,就开始执行 thenRun,只是处理完任务后,执行 thenRun 的后续操作

    带有 Async 默认是异步执行的。同之前。

    以上都要前置任务成功完成。 Function

    • T:上一个任务返回结果的类型
    • U:当前任务的返回值类型

    3.4 两任务组合-都要完成

    两个任务必须都完成,触发该任务。

    • thenCombine:组合前两个 future,获取前两个 future 的返回结果,并返回当前任务的返回值
    • thenAcceptBoth:组合前两个 future,获取前两个 future 任务的返回结果,然后处理任务,没有 返回值。
    • runAfterBoth:组合前两个 future,不需要获取 future 的结果,只需前两个 future 处理完任务后, 处理该任务。

    3.5 两任务组合-一个完成

    当两个任务中,任意一个 future 任务完成的时候,执行任务。

    • applyToEither:两个任务有一个执行完成,获取它的返回值,处理任务并有新的返回值。
    • acceptEither:两个任务有一个执行完成,获取它的返回值,处理任务,没有新的返回值。
    • runAfterEither:两个任务有一个执行完成,不需要获取 future 的结果,处理任务,也没有返 回值

    3.6 多任务组合

    • allOf:等待所有任务完成
    • anyOf:只要有一个任务完成

    八、商品详情

    配置详情页路由

    image-20220728145905077

    1. 封装vo

    封装一个详情页的skuvo

    @ToString
    @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;
    
        //6、秒杀商品的优惠信息
        //private SeckillSkuVo seckillSkuVo;
    }
    
    @Data
    @ToString
    public class SkuItemSaleAttrVo {
    
        private Long attrId;
    
        private String attrName;
    
        private List<AttrValueWithSkuIdVo> attrValues;
        //private String attrValue 属性值
        //private String skuIds 该属性值对应的skuId的集合
    
    }
    
    @Data
    @ToString
    public class SpuItemAttrGroupVo {
    
        private String groupName;
    
        //attrId,attrName,attrValue
        private List<Attr> attrs;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    2.使用异步编排提高效率

    @Override
        public SkuItemVo item(Long skuId) {
            SkuItemVo skuItemVo = new SkuItemVo();
            CompletableFuture<SkuInfoEntity> infoFuture = CompletableFuture.supplyAsync(() -> {
                //1、sku基本信息的获取  pms_sku_info
                SkuInfoEntity skuInfoEntity = this.getById(skuId);
                skuItemVo.setInfo(skuInfoEntity);
                return skuInfoEntity;
            }, executor);
    
            //2、sku的图片信息    pms_sku_images
            CompletableFuture<Void> imageFuture = CompletableFuture.runAsync(() -> {
                List<SkuImagesEntity> skuImagesEntities = skuImagesService.list(new QueryWrapper<SkuImagesEntity>().eq("sku_id", skuId));
                skuItemVo.setImages(skuImagesEntities);
            }, executor);
    
    
            //3、获取spu的销售属性组合-> 依赖1 获取spuId
            CompletableFuture<Void> saleFuture = infoFuture.thenAcceptAsync((info) -> {
                List<SkuItemSaleAttrVo> saleAttrVos = skuSaleAttrValueService.listSaleAttrs(info.getSpuId());
                skuItemVo.setSaleAttr(saleAttrVos);
            }, executor);
    
    
            //4、获取spu的介绍-> 依赖1 获取spuId
            CompletableFuture<Void> descFuture = infoFuture.thenAcceptAsync((info) -> {
                SpuInfoDescEntity byId = spuInfoDescService.getById(info.getSpuId());
                skuItemVo.setDesc(byId);
            }, executor);
    
    
            //5、获取spu的规格参数信息-> 依赖1 获取spuId catalogId
            CompletableFuture<Void> attrFuture = infoFuture.thenAcceptAsync((info) -> {
                List<SpuItemAttrGroupVo> spuItemAttrGroupVos=productAttrValueService.getProductGroupAttrsBySpuId(info.getSpuId(), info.getCatalogId());
                skuItemVo.setGroupAttrs(spuItemAttrGroupVos);
            }, executor);
    
    //        //6、秒杀商品的优惠信息
    //        CompletableFuture seckFuture = CompletableFuture.runAsync(() -> {
    //            R r = seckillFeignService.getSeckillSkuInfo(skuId);
    //            if (r.getCode() == 0) {
    //                SeckillSkuVo seckillSkuVo = r.getData(new TypeReference() {
    //                });
    //                long current = System.currentTimeMillis();
    //                //如果返回结果不为空且活动未过期,设置秒杀信息
    //                if (seckillSkuVo != null&¤t
    //                    skuItemVo.setSeckillSkuVo(seckillSkuVo);
    //                }
    //            }
    //        }, executor);
    
            //等待所有任务执行完成
            try {
                CompletableFuture.allOf(imageFuture, saleFuture, descFuture, attrFuture).get();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
    
            return skuItemVo;
        }
    
    
        /**
         * 商品详情页
         *
         * @param skuId
         * @return
         */
    /*
        @Override
        public SkuItemVo item(Long skuId) {
            SkuItemVo skuItemVo = new SkuItemVo();
            //1、sku基本信息的获取  pms_sku_info
            SkuInfoEntity skuInfoEntity = this.getById(skuId);
            skuItemVo.setInfo(skuInfoEntity);
            Long spuId = skuInfoEntity.getSpuId();
            Long catalogId = skuInfoEntity.getCatalogId();
    
    
            //2、sku的图片信息    pms_sku_images
            List skuImagesEntities = skuImagesService.list(new QueryWrapper().eq("sku_id", skuId));
            skuItemVo.setImages(skuImagesEntities);
    
            //3、获取spu的销售属性组合-> 依赖1 获取spuId
            List saleAttrVos = skuSaleAttrValueService.listSaleAttrs(spuId);
            skuItemVo.setSaleAttr(saleAttrVos);
    
            //4、获取spu的介绍-> 依赖1 获取spuId
            SpuInfoDescEntity byId = spuInfoDescService.getById(spuId);
            skuItemVo.setDesc(byId);
    
            //5、获取spu的规格参数信息-> 依赖1 获取spuId catalogId
            List spuItemAttrGroupVos = productAttrValueService.getProductGroupAttrsBySpuId(spuId, catalogId);
            skuItemVo.setGroupAttrs(spuItemAttrGroupVos);
            //TODO 6、秒杀商品的优惠信息
    
            return skuItemVo;
        }
        */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101

    image-20220729133444938

  • 相关阅读:
    Spring Security配置个过滤器也这么卷
    Day35.根据身高重建队列、最少箭数引爆气球
    英语写作中“省略”、“忽略”、“忽视”omit、ignore、neglect 的用法
    已解决:KeyError: ‘The truth value of a Series is ambiguous‘ 问题
    第一章 教育基础(03 教育目的与教育制度)
    Win11提示Windows无法访问指定设备路径或文件的三种解决方法
    Windows下实用工具汇总(更新……)
    Latex | 公式编辑
    sqlserver==索引解析,执行计划,索引大小
    高级架构师_Redis_第2章_数据类型与底层数据结构
  • 原文地址:https://blog.csdn.net/qq_52476654/article/details/126053956