检索功能

和检索的所有功能都放在search 服务下:
因为要对前端页面进行搭建:所以search 服务中需要引入thymeleaf
在这里插入图片描述

在Index 页面中,引入thymeleaf 的名称空间。
在这里插入图片描述

功能说明:
当点击搜索按钮时,就应该跳转到search 服务的index
在这里插入图片描述

同时浏览器中输入search.gulimall.com 也能进入来搜索页面。然后又要用nginx 的动静分离。所以所有的关于search 服务的静态资源都放在nginx 的html/static/search 的路径下。
在这里插入图片描述

搜索页面的html 文件就放在search 服务中
在这里插入图片描述

然后在gateway 服务中配置负责转发search 页面请求的路由。
在这里插入图片描述

在这里插入图片描述

既然要让gateway 发现并找到search 服务,那么该服务就必须在Nacos 中有注册。
search 配置Nacos 服务发现地址, 以及配置当前应用的名字
在这里插入图片描述

配置服务发现功能:
在这里插入图片描述

接下来进行项目开发:
准备:
加入热部署依赖
在这里插入图片描述

关闭thymeleaf 缓存:
在这里插入图片描述

这样修改完的前端页面就可以直接Ctrl + F9 进行重新编译。

项目中除里用域名进入搜索页面,也能可以通过三级菜单中的分类来进入搜索页面。
在这里插入图片描述

点击了三级菜单,因为它需要转到list.html ,所以就把原本search 服务的index.html 改名为list.html
在这里插入图片描述

编写一个专门处理三级菜单跳转到搜索页面的Controller
在这里插入图片描述

效果:
在这里插入图片描述

实现搜索栏输入关键字后点击搜索按钮进入搜索页面功能:
在这里插入图片描述

分析:
检索系统要检索的商品
在这里插入图片描述
应该是在经过search 页面的Controller 时获取到搜索的参数,然后再返回所需要的数据到搜索页面。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在进入搜索页面下面还有一大堆的检索条件,这些构成了复杂的检索条件。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

约定:在点击综合排序时,只能选中一个进行排序搜索。在项目中我们修改成:销量,价格,热度
在这里插入图片描述
比如我点击了销量,按照升序进行排序,那么封装成参数就是:
在这里插入图片描述
过滤参数:
在这里插入图片描述

给商品筛选的参数制定规则:
把这里面的属性都统称为:attrs
在这里插入图片描述
比如我选择:“系统”的“其他”和“安卓” -> 参数形式:attrs=1_其他:安卓
我还选择了其他的属性:attrs=1_其他:安卓&attrs=…

因为页面也需要数据进行显示,所以也要封装一个VO 对象给它
参考京东商城,每个查询结果的属性中都有两个固定的属性:品牌和分类
在这里插入图片描述

在这里插入图片描述

下面的属性都是根据商品所具有的属性来进行显示的。
在这里插入图片描述

attrName 就是像“操作系统”,“分辨率”…,他们的值就是可以被点击的部分(蓝色部分)
在这里插入图片描述

关于分类和品牌在Vo 对象中的字段:
检索参数是这样的:
在这里插入图片描述

将这些参数都分为三部分:品牌,分类,其他参数。各自都是一个类。并封装来一个List 中。
在这里插入图片描述

Controller:
在这里插入图片描述

这些查询都是去ES 中查询。

根据SearchResult 的参数(分页数据除外),制定ES 的查询

@Data
public class SearchParam {

    //从搜索框中输入的关键字
    private String keyword;

    //从三级菜单点击商品中传过来的三级分类id
    private Long catalogId;

    /**
     *  sort = saleCount_asc/desc
     *  sort = skuPrice_asc/desc
     *  sort = hotScore_asc/desc
     */
    //排序条件
    private String sort;

    /**
     * 好多的过滤条件
     * hasStock(是否有货)、skuPrice价格区间、brandId、catalog3Id
     *  hasStock=0/1
     *  skuPrice=1_500/_500/500_
     */
    private Integer hasStock; //是否只显示有货

    private String skuPrice;//价格区间

    private List<Long> brandId; //按照品牌进行查询,可以多选

    private List<String> attrs; //按照属性进行筛选

    private Integer pageNum; //页码



}

keyword 就是must 中的skuTitle;
为什么要用filter? 因为这里面的数据不用得分,查询出来会快一点
catalog3Id 就是filter 中的catalog3Id。
sort 就是filter 中的sort。
hasStock 就是filter 中的hasStcok。
brandId 就是filter 中的brandId .
attrs 就是filter 中nested 中的attrs(因为它是经过ES 的扁平化处理的,所以要用nested 才能拿出来)
如果要查询的attr 是多个那么就要写多个term,要为不同的attr 属性创建不同的attr
在这里插入图片描述

skuPrice 就是filter 中的range 的skuPrice(查询价格区间)
分页数据:
from…size…
因为在页面展示的时候,鼠标选中商品时,商品介绍会有一个高亮显示。
在这里插入图片描述

在这里插入图片描述

效果:
在这里插入图片描述

GET product/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "skuTitle": "华为"
          }
        }
      ],
      "filter": [
        {
          "term": {
            "catalogId": "225"
          }
        },
        {
          "terms": {
            "brandId": [
              "1",
              "4",
              "9"
            ]
          }
        },
        {
          "nested": {
            "path": "attrs",
            "query": {
              "bool": {
                "must": [
                  {
                    "term": {
                      "attrs.attrId": {
                        "value": "9"
                      }
                    }
                  },
                  {
                    "terms": {
                      "attrs.attrValue": [
                        "海思(Hisillcon)",
                        "HUAWEI kirin970"
                      ]
                    }
                  }
                ]
              }
            }
          }
        },
        {
          "term": {
            "hasStock": {
              "value": "true"
            }
          }
        },
        {
          "range": {
            "skuPrice": {
              "gte": 0,
              "lte": 6000
            }
          }
        }
      ]
    }
  },
  "sort": [
    {
      "skuPrice": {
        "order": "desc"
      }
    }
  ],
  "from": 0,
  "size": 1,
  "highlight": {
    "fields": {"skuTitle":{}}, 
    "pre_tags": "<b style='color:red'>",
    "post_tags": "</b>"
  }
}

最难的是在搜索页面把搜索出的商品的数据进行动态变化(即展示出的属性都是下面商品中都要有的,而且还是动态改变的,毕竟人可能无时无刻都在搜索商品)。所以以下就是聚合区的数据内容
在这里插入图片描述

所以就要分析根据条件查询出的商品都有哪些属性,将这些属性进行聚合。

所以聚合数据就分为三部分:
品牌聚合,分类聚合(可能需要显示各种商品类型),属性聚合

  "aggs": {
    "brand_agg": {
      "terms": {
        "field": "brandId",
        "size": 10
      },
      "aggs": {
        "brand_name_agg": {
          "terms": {
            "field": "brandName",
            "size": 10
          }
        },
        "brand_img_agg":{
          "terms": {
            "field": "brandImg",
            "size": 10
          }
        }
      }
    },
    "catalog_agg": {
       "terms": {
        "field": "catalogId",
        "size": 10
      },
      "aggs": {
        "catalog_name_agg": {
          "terms": {
            "field": "catalogName",
            "size": 10
          }
        }
      }
    },
    "attr_agg": {
      "nested": {
        "path": "attrs"
      },
      "aggs": {
        "attr_id_agg": {
          "terms": {
            "field": "attrs.attrId",
            "size": 10
          },
          "aggs": {
            "attr_name_agg": {
              "terms": {
                "field": "attrs.attrName",
                "size": 10
              }
            },
            "attr_value_agg": {
              "terms": {
                "field": "attrs.attrValue",
                "size": 10
              }
            }
          }
        }
      }
    }
  }

下面将上面的dsl 语句转换成java 代码:

private SearchRequest buildSearchRequest(SearchParam param) {


        //构建DSL 语句
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        /**
         * 所有的查询条件:模糊匹配,过滤(按照属性,分类,品牌,价格区间,库存)
         */
        //1.构建bool - query
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        //1.1 must - 模糊匹配
        if (!StringUtils.isEmpty(param.getKeyword())){
            boolQuery.must(QueryBuilders.matchQuery("skuTitle",param.getKeyword()));
        }

        //1.2. bool - filter 按照三级分类id 查询
        if (param.getCatalog3Id() != null){
            boolQuery.filter(QueryBuilders.termQuery("catalogId",param.getCatalog3Id()));
        }

        //1.2 bool -filter 按照品牌id 查询
        if (param.getBrandId() != null && param.getBrandId().size() > 0){
            boolQuery.filter(QueryBuilders.termsQuery("brandId",param.getBrandId()));
        }

        //1.2 bool -filter 按照所有指定的属性进行查询
        if (param.getAttrs() != null && param.getAttrs().size() > 0){
            //attrs=1_5寸:8寸&attrs=2_16G:8G
            for (String attrStr: param.getAttrs()) {
                BoolQueryBuilder nestedboolQuery = QueryBuilders.boolQuery();
                //attrStr = 1_5寸:8寸
                String[] s = attrStr.split("_");
                String attrId = s[0]; //检索属性 id

                //5寸:8寸
                String[] attrValues = s[1].split(":"); //这个属性的检索用的值
                nestedboolQuery.must(QueryBuilders.termQuery("attrs.attrId",attrId));
                nestedboolQuery.must(QueryBuilders.termsQuery("attrs.attrValue",attrValues));
                //每一个必须都得生成一个nested 查询
                NestedQueryBuilder nestedQuery = QueryBuilders.nestedQuery("attrs", nestedboolQuery, ScoreMode.None);
                boolQuery.filter(nestedQuery);
            }
        }


        //1.2 bool -filter 按照库存是否有进行查询
        boolQuery.filter(QueryBuilders.termQuery("hasStock",param.getHasStock() == 1));

        //1.2 bool -filter 按照价格区间进行查询
        if (!StringUtils.isEmpty(param.getSkuPrice())){
            //1_500/_500/500_
            RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("skuPrice");

            String[] s = param.getSkuPrice().split("_");
            if (s.length == 2){
                //证明价格区间是一个双闭合区间
                rangeQuery.gte(s[0]).lte(s[1]);
            }else if(s.length == 1){
                //证明价格区间是一个双闭合区间
                if(param.getSkuPrice().startsWith("_")){
                    rangeQuery.lte(s[0]);
                }
                if(param.getSkuPrice().endsWith("_")){
                    rangeQuery.gte(s[0]);
                }
            }

            boolQuery.filter(rangeQuery);

        }


        //把以前的所有条件都拿来进行封装
        sourceBuilder.query(boolQuery);

        /**
         * 排序,分页,高亮
         */
        //2.1 排序
        if(!StringUtils.isEmpty(param.getSort())){
            String sort = param.getSort();
            //sort = hotSocre_asc/desc
            String[] s = sort.split("_");
            SortOrder order = s[1].equalsIgnoreCase("asc")?SortOrder.ASC : SortOrder.DESC;
            sourceBuilder.sort(s[0],order);
        }

        //2.2 分页    pageSize = 5
        // pageNum=1, from=0, size=5, [0,1,2,3,4]
        // pageNum=2, from=5, size=5
        // 计算from 的公式: from = (pageNum - 1)*size
        sourceBuilder.from((param.getPageNum() - 1) * EsConstant.PRODUCT_PAGESIZE);
        sourceBuilder.size(EsConstant.PRODUCT_PAGESIZE);

        //2.3 高亮: 只有经过模糊查询的数据才需要高亮显示
        if (!StringUtils.isEmpty(param.getKeyword())){
            HighlightBuilder builder = new HighlightBuilder();
            builder.field("skuTitle");
            builder.preTags("<b style='color:red'>");
            builder.postTags("</b>");
            sourceBuilder.highlighter(builder);
        }

        /**
         * 聚合分析
         */
        //品牌聚合
        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);

        //2. 分类聚合 catalog_agg
        TermsAggregationBuilder catalog_agg = AggregationBuilders.terms("catalog_agg").field("catalogId").size(20);
        catalog_agg.subAggregation(AggregationBuilders.terms("catalog_name_agg").field("catalogName").size(1));
        sourceBuilder.aggregation(catalog_agg);

        //3. 属性聚合 attr_agg
        //声明是嵌入式聚合
        NestedAggregationBuilder attr_agg = AggregationBuilders.nested("attr_agg", "attrs");
        //聚合当前的attr_id
        TermsAggregationBuilder attr_id_agg = AggregationBuilders.terms("attr_id_agg").field("attrs.attrId");
        //根据父聚合得出的attr_id 得出他们的attr_name
        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("构建的DSL:"+s);
        SearchRequest searchRequest = new SearchRequest(new String[]{EsConstant.PRODUCT_INDEX}, sourceBuilder);

        return searchRequest;
    }

这段对应的前端代码:
在这里插入图片描述

 <div class="rig_tab">
                        <div th:each="product:${result.getProducts()}">
                            <div class="ico">
                                <i class="iconfont icon-weiguanzhu"></i>
                                <a href="/static/search/#">关注</a>
                            </div>
                            <p class="da">
                                <a href="/static/search/#">
                                    <img th:src="${product.skuImg}" class="dim">
                                </a>
                            </p>
                            <ul class="tab_im">
                                <li><a href="/static/search/#" title="黑色">
                                    <img th:src="${product.skuImg}"></a></li>
                            </ul>
                            <p class="tab_R">
                                <span th:text="'¥'+${product.skuPrice}">¥5199.00</span>

                            </p>
                            <p class="tab_JE">
                                <a href="/static/search/#" th:utext="${product.skuTitle}">
                                    Apple iPhone 7 Plus (A1661) 32G 黑色 移动联通电信4G手机
                                </a>
                            </p>
                            <p class="tab_PI">已有<span>11+</span>热门评价
                                <a href="/static/search/#">二手有售</a>
                            </p>
                            <p class="tab_CP"><a href="/static/search/#" title="谷粒商城Apple产品专营店">谷粒商城Apple产品...</a>
                                <a href='#' title="联系供应商进行咨询">
                                    <img src="/static/search/img/xcxc.png">
                                </a>
                            </p>
                            <div class="tab_FO">
                                <div class="FO_one">
                                    <p>自营
                                        <span>谷粒商城自营,品质保证</span>
                                    </p>
                                    <p>满赠
                                        <span>该商品参加满赠活动</span>
                                    </p>
                                </div>
                            </div>
                        </div >
                    </div>

还有这段对应的前端:
在这里插入图片描述

前端代码:

 <div class="JD_selector">
            <!--手机商品筛选-->
            <div class="title">
                <h3><b>手机</b><em>商品筛选</em></h3>
                <div class="st-ext">&nbsp;<span>10135</span>个商品 </div>
            </div>
            <div class="JD_nav_logo">
                <!--品牌-->
                <div class="JD_nav_wrap">
                    <div class="sl_key">
                        <span><b>品牌:</b></span>
                    </div>
                    <div class="sl_value">
                        <div class="sl_value_logo">
                            <ul>
                                <li th:each="brand:${result.brands}">
                                    <a href="/static/search/#">
                                        <img th:src="${brand.brandImg}" alt="">
                                        <div th:text="${brand.brandName}">
                                            华为(HUAWEI)
                                        </div>
                                    </a>
                                </li>

                            </ul>
                        </div>
                    </div>
                    <div class="sl_ext">
                        <a href="/static/search/#">
                            更多
                            <i style='background: url("image/search.ele.png")no-repeat 3px 7px'></i>
                            <b style='background: url("image/search.ele.png")no-repeat 3px -44px'></b>
                        </a>
                        <a href="/static/search/#">
                            多选
                            <i>+</i>
                            <span>+</span>
                        </a>
                    </div>
                </div>

                <!--分类-->
                <div class="JD_pre">
                    <div class="sl_key">
                        <span><b>分类:</b></span>
                    </div>
                    <div class="sl_value">
                            <ul>
                                <li th:each="catalog:${result.catalogs}">
                                    <a href="/static/search/#" th:text="${catalog.catalogName}">5.56英寸及以上</a>
                                </li>
                            </ul>
                    </div>
                    <div class="sl_ext">
                        <a href="/static/search/#">
                            更多
                            <i style='background: url("image/search.ele.png")no-repeat 3px 7px'></i>
                            <b style='background: url("image/search.ele.png")no-repeat 3px -44px'></b>
                        </a>
                        <a href="/static/search/#">
                            多选
                            <i>+</i>
                            <span>+</span>
                        </a>
                    </div>
                </div>

                <!--其他的所有需要展示的属性-->
                <div class="JD_pre" th:each="attr:${result.attrs}">
                    <div class="sl_key">
                        <span th:text="${attr.attrName}">屏幕尺寸:</span>
                    </div>
                    <div class="sl_value">
                        <ul>
                            <li th:each="val:${attr.attrValue}"><a href="/static/search/#" th:text="${val}">5.56英寸及以上</a></li>
                        </ul>
                    </div>
                </div>


            </div>

实现效果:当点击“品牌” 和“分类” 中的数据时,在url 上就要加上对应的参数
在这里插入图片描述

为拼接url 写一个方法:
在这里插入图片描述

品牌的跳转代码:
在这里插入图片描述

分类的跳转代码:
在这里插入图片描述

其他属性的跳转代码:由于其他属性的参数是一个拼接出来的字符串,所以它的参数也要加上双引号:
在这里插入图片描述

完成功能:在输入框输入关键字,点击搜索,就能在下面查询出相关商品
在这里插入图片描述

对应代码:

在这里插入图片描述
其实还是调用商品属性的那个拼接url 的方法
在这里插入图片描述

完成功能:
分页条
在这里插入图片描述

这里一个巧妙的点:在a 标签中创建一个属性pn, 可以用它来操作并记录当前页码:pageNum。
在这里插入图片描述
这些a 标签的跳转函数。
在这里插入图片描述

确定按钮所绑定页数输入框的跳转函数:
在这里插入图片描述

完成功能:在搜索栏输入关键字进行搜索,并且回显输入的信息在搜索框
搜索功能:
在这里插入图片描述
图中的th:value="${param.keyword}",这里是的param 是url 中的参数,这是thymeleaf 的语法。所以这里就完成了回显

搜索函数:
在这里插入图片描述

完成功能:商品排序
在这里插入图片描述

比如:当我们点击销量,发送的请求参数是:sort=saleCount_asc/desc
在这里插入图片描述
在这里插入图片描述

而当点击对应排序按钮而显示的红色高亮显示的样式,因为它是在页面跳转以后才能显示的,所以在跳转前给它设置的样式都没用,所以就要用另一种方法
给它自定义一个style 样式:然后用三目运算算出它应该有的样式

 <div class="filter_top">
                        <div class="filter_top_left" th:width="p = ${param.sort}">
                            <a class="sort_a"
                               th:attr="style=${(#strings.isEmpty(p) || #strings.startsWith(p,'hotScore'))?'color: #333;border-color: #CCC; background: #FFF':'color: #FFF;border-color: #4393c; background: #e4393c'}"
                               sort="hostScore"  href="/static/search/#">综合排序</a>
                            <a class="sort_a"
                               th:attr="style=${(!#strings.isEmpty(p) && #strings.startsWith(p,'saleCount'))?'color: #333;border-color: #CCC; background: #FFF':'color: #FFF;border-color: #4393c; background: #e4393c'}"
                               sort="saleCount" href="/static/search/#">销量</a>
                            <a class="sort_a"
                               th:attr="style=${(!#strings.isEmpty(p) && #strings.startsWith(p,'skuPrice'))?'color: #333;border-color: #CCC; background: #FFF':'color: #FFF;border-color: #4393c; background: #e4393c'}"
                               sort="skuPrice"  href="/static/search/#">价格</a>
                            <a class="sort_a"  href="/static/search/#">评论分</a>
                            <a class="sort_a" href="/static/search/#">上架时间</a>
                        </div>

给a 标签的父标签定义一个变量:p,它所获取的就是参数中的sort 的值
在这里插入图片描述

因为页面进行了刷新,所以在方法中设置的desc 在刷新后的页面都没有了,所以class 属性中的值也要进行回显。可以根据发送的sort 参数的值来进行class 的回显。

 <div class="filter_top_left" th:width="p = ${param.sort}">
                            <a th:class="${(!#strings.isEmpty(p) && #strings.startsWith(p,'hotScore') && #strings.endsWith(p,'desc'))}?'sort_a desc':'sort_a'"
                               th:attr="style=${(#strings.isEmpty(p) || #strings.startsWith(p,'hotScore'))?'color: #333;border-color: #CCC; background: #FFF':'color: #FFF;border-color: #4393c; background: #e4393c'}"
                               sort="hostScore"  href="/static/search/#">综合排序</a>
                            <a th:class="${(!#strings.isEmpty(p) && #strings.startsWith(p,'saleCount') && #strings.endsWith(p,'desc'))}?'sort_a desc':'sort_a'"
                               th:attr="style=${(!#strings.isEmpty(p) && #strings.startsWith(p,'saleCount'))?'color: #333;border-color: #CCC; background: #FFF':'color: #FFF;border-color: #4393c; background: #e4393c'}"
                               sort="saleCount" href="/static/search/#">销量</a>
                            <a th:class="${(!#strings.isEmpty(p) && #strings.startsWith(p,'skuPrice') && #strings.endsWith(p,'desc'))}?'sort_a desc':'sort_a'"
                               th:attr="style=${(!#strings.isEmpty(p) && #strings.startsWith(p,'skuPrice'))?'color: #333;border-color: #CCC; background: #FFF':'color: #FFF;border-color: #4393c; background: #e4393c'}"
                               sort="skuPrice"  href="/static/search/#">价格</a>
                            <a class="sort_a"  href="/static/search/#">评论分</a>
                            <a class="sort_a" href="/static/search/#">上架时间</a>
                        </div>

在这里插入图片描述

给排序的文本内容加上:↑,↓

 <div class="filter_top">
                        <div class="filter_top_left" th:width="p = ${param.sort}">
                            <a th:class="${(!#strings.isEmpty(p) && #strings.startsWith(p,'hotScore') && #strings.endsWith(p,'desc'))?'sort_a desc':'sort_a'}"
                               th:attr="style=${(#strings.isEmpty(p) || #strings.startsWith(p,'hotScore'))?'color: #333;border-color: #CCC; background: #FFF':'color: #FFF;border-color: #4393c; background: #e4393c'}"
                               sort="hostScore"  href="/static/search/#">综合排序 [[${(!#strings.isEmpty(p) && #strings.startsWith(p,'hotScore') && #strings.endsWith(p,'desc'))?'↓':'↑'}]]</a>
                            <a th:class="${(!#strings.isEmpty(p) && #strings.startsWith(p,'saleCount') && #strings.endsWith(p,'desc'))?'sort_a desc':'sort_a'}"
                               th:attr="style=${(!#strings.isEmpty(p) && #strings.startsWith(p,'saleCount'))?'color: #333;border-color: #CCC; background: #FFF':'color: #FFF;border-color: #4393c; background: #e4393c'}"
                               sort="saleCount" href="/static/search/#">销量  [[${(!#strings.isEmpty(p) && #strings.startsWith(p,'saleCount') && #strings.endsWith(p,'desc'))?'↓':'↑'}]]</a>
                            <a th:class="${(!#strings.isEmpty(p) && #strings.startsWith(p,'skuPrice') && #strings.endsWith(p,'desc'))?'sort_a desc':'sort_a'}"
                               th:attr="style=${(!#strings.isEmpty(p) && #strings.startsWith(p,'skuPrice'))?'color: #333;border-color: #CCC; background: #FFF':'color: #FFF;border-color: #4393c; background: #e4393c'}"
                               sort="skuPrice"  href="/static/search/#">价格  [[${(!#strings.isEmpty(p) && #strings.startsWith(p,'skuPrice') && #strings.endsWith(p,'desc'))?'↓':'↑'}]]</a>
                            <a class="sort_a"  href="/static/search/#">评论分</a>
                            <a class="sort_a" href="/static/search/#">上架时间</a>
                        </div>

完成功能:价格区间
在这里插入图片描述
组装参数函数:
在这里插入图片描述

页面跳转后回显参数:
在这里插入图片描述

完成功能:过滤查询条件

回显功能:
在这里插入图片描述

封装函数并跳转页面功能:
在这里插入图片描述

完成功能:面包屑导航
当点击一个属性进行查询时,这个属性就会增添到上面的一个导航块中,并且当删除一个导航块,页面就会回到上一个点击属性的页面中
在这里插入图片描述

远程调用需要的包:
1.spring-cloud
1.1 spring-cloud 版本
在这里插入图片描述

1.2 spring-cloud 依赖
在这里插入图片描述

2.open-feign 依赖
在这里插入图片描述

3.主程序中,配置注释开启远程调用
在这里插入图片描述

分析:面包屑中的数据,并构造出该Vo 类
在这里插入图片描述

编写远程调用接口
在这里插入图片描述

对应被远程调用的方法
在这里插入图片描述

可以看到返回的类型是AttrRespVo.
AttrRespVo
在这里插入图片描述
AttrVo
在这里插入图片描述

因为远程调用的方法的返回值是AttrRespVo,但是这个类型只属于product 类型,所以要么把AttrRespVo 放到common 类中,要么在search 类中再构建一个包含了AttrRespVo 类型包括它的父类数据的类。
在这里插入图片描述
从前端传来的数据param 中分析出面包屑所需要的参数并构建出来
在这里插入图片描述

当取消掉最新的面包屑之后,要跳转到上一个点击面包屑的参数页面中。
这里是这样设计的,每当点击一个属性之后,就生成一个面包屑数据,而这个面包屑数据就包含上一个属性作为参数的路径。作为前端:只需要把面包屑的数据都遍历出来,然后封装在一个a 标签中即可,当点击a 标签时,就触发面包屑数据的link 属性(上一个属性所在的url)
在这里插入图片描述
在这里插入图片描述

因为attr 属性是在url 中可以连续存在的,即url?attrs=xxxx&attrs=xxxx…
所以重写了替换和添加参数的方法:
在这里插入图片描述
在这里插入图片描述

前端构建面包屑效果:
当点击"x" ,就是触发了a 标签中的href。其中就包含了nav.link
在这里插入图片描述

完善面包屑导航功能:
当点击属性让它成为面包屑导航以后,它的属性所在的属性栏中消失。
在这里插入图片描述

1.其他的所有都能上面包屑导航,即品牌分类都可以
在这里插入图片描述

完善功能:
当点击了品牌,属性,分类成为了面包屑导航以后,对应在属性栏中的会消失。
因为属性栏在前端页面也要在成为面包屑后消失,所以要知道是哪个属性对应的id,所以就要在Result 中封装一个attrIds.
在这里插入图片描述

因为品牌等参数都要获取,所以获取url 的字符串。通过Servlet 的request 就可以就能够进行获取
在这里插入图片描述

前端:检测到参数中有没有brandId ,有则不显示品牌栏
在这里插入图片描述

属性栏:遍历到所有属性,然后将url 中出现的属性抹除
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值