谷粒商城-分布式高级篇[商城业务-检索服务]

  1. 谷粒商城-分布式基础篇【环境准备】
  2. 谷粒商城-分布式基础【业务编写】
  3. 谷粒商城-分布式高级篇【业务编写】持续更新
  4. 谷粒商城-分布式高级篇-ElasticSearch
  5. 谷粒商城-分布式高级篇-分布式锁与缓存
  6. 项目托管于gitee

一、商城业务-检索服务

确保gulimall-search 服务开启注册中心并加入到nacos中

gulimall-search 服务下:

1.1、搭建页面环境


1.1.1、动静资源配置

动静分离

  1. 给gulimall-search服务加入依赖Thymeleaf依赖

    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-thymeleafartifactId>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-devtoolsartifactId>
    dependency>
    
  2. 2.分布式高级篇/代码/html/搜索页路径下的 index.html首页复制到 gulimall-search服务src/main/resources/templates路径下

  3. 2.分布式高级篇/代码/html/搜索页路径下的 所有其他静态资源复制到 nginx 的

    hgw@HGWdeAir search % pwd
    /Users/hgw/Documents/Software/mydata/nginx/html/static/search
    hgw@HGWdeAir search % ll
    total 0
    drwxrwxr-x@  4 hgw  staff   128B  3 22  2020 css
    drwxrwxr-x@ 12 hgw  staff   384B  3 22  2020 font
    drwxrwxr-x@ 68 hgw  staff   2.1K  3 22  2020 image
    drwxrwxr-x@ 86 hgw  staff   2.7K  3 22  2020 img
    drwxrwxr-x@  4 hgw  staff   128B  3 22  2020 js
    drwxrwxr-x@  3 hgw  staff    96B  3 22  2020 sass
    
  4. 修改index.html 的静态资源请求路径

    href="		-->		href="/static/search/
    src="			-->		src="/static/search/
    

1.1.2、配置 Nginx 和 网关

配置 Nginx 和 网关

谷粒商城-分布式高级篇[商城业务-检索服务]_第1张图片

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

    # Gulimall Host Start
    127.0.0.1 gulimall.cn
    127.0.0.1 search.gulimall.cn
    # Gulimall Host End
    
  2. 配置Nginx(将search下的请求转给网关)

    hgw@HGWdeAir conf.d % pwd
    /Users/hgw/Documents/Software/mydata/nginx/conf/conf.d
    hgw@HGWdeAir conf.d % vim gulimall.conf 
    
    server {
        listen       80;
        server_name  gulimall.cn  *.gulimall.cn;
    

    重启nginx容器:docker restart nginx

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

            - id: gulimall_host_route
              uri: lb://gulimall-product
              predicates:
                - Host=gulimall.cn
    
            - id: gulimall_search_route
              uri: lb://gulimall-search
              predicates:
                - Host=search.gulimall.cn
    

访问 :http://search.gulimall.cn/ ,成功跳转

1.1.3、调整页面跳转

调整页面跳转

修改gulimall-search服务中的index.html文件名修改为 list.html,并

  1. 修改的list.html中首页的请求路径为:http://gulimall.cn

  2. 修改nginx 静态资源路径下的mydata/nginx/html/static/index/js 下的 catalogLoader.js 中的gmall请求为你自己的请求:

    var cata3link = $("+ctg3.id+"\" style=\"color: #999;\">" + ctg3.name + "");
    
  3. 搜索 search() ,进行修改

    • 第一处:
    <a href="javascript:search();"><img src="/static/index/img/img_09.png" /></a>
    
    • 第二处:
    function search() {
      	var keyword=$("#searchText").val()
      	window.location.href="http://search.gulimall.cn/list.html?keyword="+keyword;
    }
    

1.1.3、调整页面跳转

调整页面跳转

修改gulimall-search服务中的index.html文件名修改为 list.html,并

  1. 修改的list.html中首页的请求路径为:http://gulimall.cn

  2. 修改nginx 静态资源路径下的mydata/nginx/html/static/index/js 下的 catalogLoader.js 中的gmall请求为你自己的请求:

    var cata3link = $("+ctg3.id+"\" style=\"color: #999;\">" + ctg3.name + "");
    
  3. 搜索 search() ,进行修改

    • 第一处:
    <a href="javascript:search();"><img src="/static/index/img/img_09.png" /></a>
    
    • 第二处:
    function search() {
      	var keyword=$("#searchText").val()
      	window.location.href="http://search.gulimall.cn/list.html?keyword="+keyword;
    }
    

1.2、搜索条件、返回结果分析


1.2.1、搜索条件分析

搜索条件分析

商品检索三个入口:

  • 1)、选择分类进入商品检索
    谷粒商城-分布式高级篇[商城业务-检索服务]_第2张图片

  • 2)、输入检索关键字展示检索页
    谷粒商城-分布式高级篇[商城业务-检索服务]_第3张图片

  • 3)、选择筛选条件进入
    谷粒商城-分布式高级篇[商城业务-检索服务]_第4张图片


  1. 全文检索:skuTitle
  2. 排序:saleCount(销量)、hotScore(热度分)、skuPrice(价格)
  3. 过滤:hasStock、skuPrice区间、brandld、catalog3Id、attrs
  4. 聚合:attrs

完整查询参数:catalog3Id=225&keyword=小米&sort=saleCount_desc/asc&hasStock=0/1&brandId=1&brandId=2&attrs=1_其他:安卓&attrs=2_5寸

封装页面所有可能传递过来的查询条件 com.atguigu.gulimall.search.vo 路径下

@Data
public class SearchParam {
    private String keyword;   // 页面传递过来的检索参数,相当于全文匹配关键字
    private Long catalog3Id;  // 三级分类的id

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

    /**
     * 好多的过滤条件
     *  hasStock、skuPrice区间、brandId、catalog3Id、
     *  hasStock=0/1
     *  skuPrice=1_500/_500/500_
     *  brandId=1
     *
     */
    private Integer hasStock=1;       // 是否只显示有货 v 0(无库存) 1(有库存)
    private String skuPrice;        // 价格区间查询
    private List<Long> brandId;     // 按照品牌进行查询,可以多选
    private List<String> attrs;     // 按照属性进行筛选
    private Integer pageNum=1;        // 页码 默认第1页
}

1.2.2、检索返回结果分析

封装检索返回结果 com.atguigu.gulimall.search.vo 路径下

public class SearchResult {

    // 查询到的所有商品信息
    private List<SkuEsModel> products;


    /**
     * 分页信息
     */
    private Integer pageNum;   // 当前页码
    private Long total;        // 总记录数
    private Integer totalPages;// 总页码
    private List<Integer> pageNavs; // 导航页码

    private List<BrandVo> brands;       // 当前查询到的结果,所有涉及到的品牌
    private List<CatalogVo> ctatLogs;   // 当前查询到的结果,所有涉及到的分类
    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.3、DSL分析


  • 模糊匹配
  • 过滤(按照分类、品牌、属性、价格区间、库存)
  • 排序、
  • 分页、
  • 高亮
  • 聚合分析
GET gulimall_product/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "skuTitle": "华为"
          }
        }
      ],
      "filter": [
        {
          "term": {
            "catalogId": "225"
          }
        },
        {
          "terms": {
            "brandId": [
              "9",
              "7",
              "10"
            ]
          }
        },
        {
          "nested": {
            "path": "attrs",
            "query": {
              "bool": {
                "must": [
                  {
                    "term": {
                      "attrs.attrId": {
                        "value": "8"
                      }
                    }
                  },
                  {
                    "terms": {
                      "attrs.attrValue": [
                        "海斯 (Hisilicon)",
                        "以官网信息为准"
                      ]
                    }
                  }
                ]
              }
            }
          }
        },
        {
          "term": {
            "hasStock": {
              "value": "true"
            }
          }
        },
        {
          "range": {
            "skuPrice": {
              "gte": 0,
              "lte": 6000
            }
          }
        }
      ]
    }
  },
  "sort": [
    {
      "skuPrice": {
        "order": "desc"
      }
    }
  ],
  "from": 0,
  "size": 1,
  "highlight": {
    "fields": {"skuTitle": {}}, 
    "pre_tags": "",
    "post_tags": ""
  },
  "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
          }
        }
      }
    },
    "catelog_agg": {
      "terms": {
        "field": "catalogId",
        "size": 10
      },
      "aggs": {
        "catalog_name_agg": {
          "terms": {
            "field": "catalogName",
            "size": 10
          }
        }
      }
    },
    "attrs_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
              }
            }
          }
        }
      }
    }
  }
}

这里由于需要使用到 brandName 进行检索,需要修改映射,在此进行数据迁移

删掉映射里的所有:

"index" : false,
"doc_values" : false
  1. 创建一个新的映射

    PUT gulimall_product
    {
      "mappings": {
        "properties": {
          "attrs": {
            "type": "nested",
            "properties": {
              "attrId": {
                "type": "long"
              },
              "attrName": {
                "type": "keyword"
              },
              "attrValue": {
                "type": "keyword"
              }
            }
          },
          "brandId": {
            "type": "long"
          },
          "brandImg": {
            "type": "keyword"
          },
          "brandName": {
            "type": "keyword"
          },
          "catalogId": {
            "type": "long"
          },
          "catalogName": {
            "type": "keyword"
          },
          "hasStock": {
            "type": "boolean"
          },
          "hotScore": {
            "type": "long"
          },
          "saleCount": {
            "type": "long"
          },
          "skuId": {
            "type": "long"
          },
          "skuImg": {
            "type": "keyword"
          },
          "skuPrice": {
            "type": "keyword"
          },
          "skuTitle": {
            "type": "text",
            "analyzer": "ik_smart"
          },
          "spuId": {
            "type": "keyword"
          }
        }
      }
    }
    
  2. 数据转移

    # 迁移数据
    POST _reindex
    {
      "source": {
        "index": "product"
      },
      "dest": {
        "index": "gulimall_product"
      }
    }
    
  3. 修改 gulimall-search 服务中 sku数据在es中的索引 的常量

    package com.atguigu.gulimall.search.constant;
    
    public class EsConstant {
    
        public static final String PRODUCT_INDEX = "gulimall_product";   // sku数据在es中的索引
    }
    

1.4、SearchRequest构建


总方法:

  1. 准备检索请求
  2. 执行检索请求
  3. 分析相应数据并封装成需要的指定格式
@Service
public class MallSearchServiceImpl implements MallSearchService {

    @Autowired
    private RestHighLevelClient client;

    @Override
    public SearchResult search(SearchParam param) {
        // 动态的构建出查询的DSL语句
        SearchResult result = null;

        // 1、准备检索请求
        SearchRequest searchRequest = buildSearchRequrest(param);

        try {
            // 2、执行检索请求
            SearchResponse response = client.search(searchRequest, GulimallElasticSearchConfig.COMMON_OPTIONS);

            // 3、分析相应数据并封装成需要的指定格式
            result = buildSearchResult(response,param);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

准备检索请求

  • 查询:模糊匹配,过滤(按照分类、品牌、属性、库存、价格区间)
  • match-模糊匹配
  • filter-过滤(按照分类、品牌、属性、库存、价格区间)
    • 按照三级分类id查询
    • 按照品牌id查询
    • 按照所有指定的属性进行查询
    • 按照是否拥有库存进行查询
    • 按照价格区间进行查询
  • 排序
  • 分页
  • 高亮
  • 聚合分析
    • 品牌聚合
    • 分类聚合
    • attr聚合
/**
 * 准备检索请求
 * 模糊匹配:过滤(按照属性、分类、品牌、价格区间、库存)、排序、分页、高亮,聚合分析
 * @return SearchRequest
 */
private SearchRequest buildSearchRequrest(SearchParam param) {
    // 构建DSL语句的
    SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

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

    // 1.2、filter-过滤(按照分类、品牌、属性、库存、价格区间)
    // 1.2.1、按照三级分类id查询
    if (param.getCatalog3Id() != null) {
        boolBuilder.filter(QueryBuilders.termQuery("catalogId", param.getCatalog3Id()));
    }
    // 1.2.2、按照品牌id查询
    if (param.getBrandId() != null && param.getBrandId().size()>0) {
        boolBuilder.filter(QueryBuilders.termsQuery("brandId", param.getBrandId()));
    }
    // 1.2.3、按照所有指定的属性进行查询
    if (param.getAttrs()!=null && param.getAttrs().size()>0) {
        for (String attrStr : param.getAttrs()) {
            // attrs=1_其他:安卓&attrs=2_5寸:1.5寸
            BoolQueryBuilder nestedBoolBuilder = QueryBuilders.boolQuery();
            String[] s = attrStr.split("_");
            String attrId = s[0];   // 检索的属性id
            String[] attrValues = s[1].split(":");  // 这个属性检索用的值
            nestedBoolBuilder.must(QueryBuilders.termQuery("attrs.attrId",attrId));
            nestedBoolBuilder.must(QueryBuilders.termsQuery("attrs.attrValue",attrValues));
            // 每一个必须都得生成一个嵌入式的查询nested
            NestedQueryBuilder nestedQuery = QueryBuilders.nestedQuery("attrs", nestedBoolBuilder, ScoreMode.None);
            boolBuilder.filter(nestedQuery);
        }
    }
    // 1.2.4、按照是否拥有库存进行查询
        if (param.getHasStock() != null) {
            boolBuilder.filter(QueryBuilders.termQuery("hasStock", param.getHasStock()==1));
        }
    // 1.2.5、按照价格区间进行查询
    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]);
            }
        }
        boolBuilder.filter(rangeQuery);
    }

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

    /**
     * 排序、分页、高亮
     */
    // 2、排序
    if (!StringUtils.isEmpty(param.getSort())){
        String sort = param.getSort();
        // sort=saleCount_desc/asc
        String[] s = sort.split("_");
        SortOrder order = s[1].equalsIgnoreCase("asc")?SortOrder.ASC:SortOrder.DESC;
        sourceBuilder.sort(s[0], order);
    }
    // 3、分页
    sourceBuilder.from((param.getPageNum()-1)*EsConstant.PRODUCT_PAGESIZE);
    sourceBuilder.size(EsConstant.PRODUCT_PAGESIZE);
    // 4、高亮
    if (!StringUtils.isEmpty(param.getKeyword())) {
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("skuTitle");
        highlightBuilder.preTags("");
        highlightBuilder.postTags("");
        sourceBuilder.highlighter(highlightBuilder);
    }


    /**
     * 聚合分析
     */
    // 5.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));
    // TODO 1、聚合brand
    sourceBuilder.aggregation(brand_agg);
    // 5.2、分类聚合
    TermsAggregationBuilder catalog_agg = AggregationBuilders.terms("catalog_agg").field("catalogId").size(20);
    catalog_agg.subAggregation(AggregationBuilders.terms("catalog_name_agg").field("catalogName").size(1));
    // TODO 2、聚合catalog
    sourceBuilder.aggregation(catalog_agg);
    // 5.3、属性聚合
    NestedAggregationBuilder attrs_agg = AggregationBuilders.nested("attrs_agg", "attrs"); // 嵌入聚合
    // 聚合出当前所有的attrId
    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对应所有可能的属性值attrValue
    attr_id_agg.subAggregation(AggregationBuilders.terms("attr_value_agg").field("attrs.attrValue").size(50));
    attrs_agg.subAggregation(attr_id_agg);
    // TODO 3、聚合attr
    sourceBuilder.aggregation(attrs_agg);

    SearchRequest searchRequest = new SearchRequest(new String[]{EsConstant.PRODUCT_INDEX}, sourceBuilder);
    return searchRequest;
}

构建结果数据

  • 返回的所有查询到的商品
  • 当前所有商品涉及到的所有属性信息
  • 保存当前商品所涉及的所有品牌信息
  • 保存当前商品所涉及的所有分类信息
  • 分页信息
/**
 * 构建结果数据
 * @param response  执行检索请求获得的响应数据
 * @return  封装成需要的指定格式并返回
 */
private SearchResult buildSearchResult(SearchResponse response, SearchParam param) {

    SearchResult result = new SearchResult();

    // 1、返回的所有查询到的商品
    SearchHits hits = response.getHits();
    List<SkuEsModel> esModels = new ArrayList<>();
    if (hits.getHits()!=null && hits.getHits().length>0) {
        for (SearchHit hit : hits.getHits()) {
            String sourceAsString = hit.getSourceAsString();
            SkuEsModel esModel = JSON.parseObject(sourceAsString, SkuEsModel.class);
            if (!StringUtils.isEmpty(param.getKeyword())) {
                HighlightField skuTitle = hit.getHighlightFields().get("skuTitle");
                String string = skuTitle.getFragments()[0].string();
                esModel.setSkuTitle(string);
            }
            esModels.add(esModel);
        }
    }
    result.setProducts(esModels);


    // 2、当前所有商品涉及到的所有属性信息
    List<SearchResult.AttrVo> attrVos = new ArrayList<>();
    ParsedNested attrs_agg = response.getAggregations().get("attrs_agg");
    ParsedLongTerms attr_id_agg = attrs_agg.getAggregations().get("attr_id_agg");
    for (Terms.Bucket bucket : attr_id_agg.getBuckets()) {
        SearchResult.AttrVo attrVo = new SearchResult.AttrVo();
        // 1、得到属性的id
        long attrId = bucket.getKeyAsNumber().longValue();
        // 2、得到属性的名字
        String attrName = ((ParsedStringTerms) bucket.getAggregations().get("attr_name_agg")).getBuckets().get(0).getKeyAsString();
        // 3、得到属性的所有值
        List<String> attrValues = ((ParsedStringTerms) bucket.getAggregations().get("attr_value_agg")).getBuckets().stream().map(item -> {
            String keyAsString = ((Terms.Bucket) item).getKeyAsString();
            return keyAsString;
        }).collect(Collectors.toList());
        attrVo.setAttrId(attrId);
        attrVo.setAttrName(attrName);
        attrVo.setAttrValue(attrValues);
        attrVos.add(attrVo);
    }
    result.setAttrs(attrVos);

    // 3、保存当前商品所涉及的所有品牌信息
    List<SearchResult.BrandVo> brandVos = new ArrayList<>();
    ParsedLongTerms brand_agg = response.getAggregations().get("brand_agg");
    for (Terms.Bucket bucket : brand_agg.getBuckets()) {
        SearchResult.BrandVo brandVo = new SearchResult.BrandVo();
        // 1、得到品牌的id
        long brandId = bucket.getKeyAsNumber().longValue();
        // 2、得到品牌的name
        String brandName = ((ParsedStringTerms) bucket.getAggregations().get("brand_name_agg")).getBuckets().get(0).getKeyAsString();
        // 3、得到品牌的img
        String brandImg = ((ParsedStringTerms) bucket.getAggregations().get("brand_img_agg")).getBuckets().get(0).getKeyAsString();
        brandVo.setBrandId(brandId);
        brandVo.setBrandImg(brandName);
        brandVo.setBrandImg(brandImg);
        brandVos.add(brandVo);
    }
    result.setBrands(brandVos);

    // 4、保存当前商品所涉及的所有分类信息
    List<SearchResult.CatalogVo> catalogVos = new ArrayList<>();
    ParsedLongTerms catalog_agg = response.getAggregations().get("catalog_agg");
    List<? extends Terms.Bucket> buckets = catalog_agg.getBuckets();
    for (Terms.Bucket bucket : buckets) {
        SearchResult.CatalogVo catalogVo = new SearchResult.CatalogVo();
        // 得到分类id
        catalogVo.setCatalogId(bucket.getKeyAsNumber().longValue());
        // 得到分类name
        ParsedStringTerms catalog_name_agg = bucket.getAggregations().get("catalog_name_agg");
        String catalog_name = catalog_name_agg.getBuckets().get(0).getKeyAsString();
        catalogVo.setCatalogName(catalog_name);
        catalogVos.add(catalogVo);
    }
    result.setCtatLogs(catalogVos);

    // 5、分页信息
    // 分页信息-页码
    result.setPageNum(param.getPageNum());
    // 分页信息-总记录数
    long total = hits.getTotalHits().value;
    result.setTotal(total);
    // 分页信息-总页码 (总记录数 对 每页数求余数)
    int totalPages = (int)total%EsConstant.PRODUCT_PAGESIZE==0 ? ((int)total/EsConstant.PRODUCT_PAGESIZE) : ((int)total/EsConstant.PRODUCT_PAGESIZE+1);
    result.setTotalPages(totalPages);
   // 分页信息-导航栏
    List<Integer> pageNavs = new ArrayList<>();
    for (int i = 1; i <= totalPages; i++) {
      pageNavs.add(i);
    }
  	result.setPageNavs(pageNavs);

    return result;
}


1.5、页面效果


1.5.1、页面基本渲染

页面基本渲染

  • 修改排序内容

    • 删掉多余的
      盒子下的三个div, 只留下一个
    • 修改价格
    • 修改图片的路径
    • 修改skuTitle
  • 修改品牌

    1. 删掉多余 li
    2. 进行修改
  • 修改规格属性

  • 增加分类信息


<div class="JD_banner w">
    <div class="JD_nav">
        <div class="JD_selector">
            
            <div class="title">
                <h3><b>手机b><em>商品筛选em>h3>
                <div class="st-ext"> <span>10135span>个商品 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.ctatLogs}"><a href="/static/search/#" th:text="${catalog.catalogName}">分类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>
            <div class="JD_show">
                <a href="/static/search/#">
                    <span>
                        更多选项( CPU核数、网络、机身颜色 等)
                    span>
                a>
            div>
        div>
        
        <div class="JD_banner_main">
            
            <div class="JD_con_left">
                <div class="JD_con_left_bar">
                    <div class="JD_con_one">
                        <div class="mt">
                            <h3>商品精选h3>
                            <span>广告span>
                        div>
                        <div class="mc">
                            <ul>
                                <li>
                                    <a href="/static/search/#" title="vivo X9s 全网通 4GB+64GB 磨砂黑 移动联通电信4G手机 双卡双待"><img src="/static/search/img/59bf3c47n91d65c73.jpg" alt="">a>
                                    <a href="/static/search/#" title="【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待">
                                        <em>华为 HUAWEI nova 2S 全面屏四摄 6GB +64GB 曜石黑 移动联通电信4G手机 双卡双待em>
                                    a>
                                    <div class="mc_price">
                                        <strong class="price">
                                            <span class="J-p-5963064">¥2999.00span>
                                        strong>
                                        <span class="mc-ico" title="购买本商品送赠品">
                                            <i class="goods-icons">赠品i>
                                        span>
                                    div>
                                    <div class="mc_rev">
                                        已有
                                        <a href="/static/search/#" class="number">12466a>
                                        人评价
                                    div>
                                li>
                                <li>
                                    <a href="/static/search/#" title="vivo X9s 全网通 4GB+64GB 磨砂黑 移动联通电信4G手机 双卡双待"><img src="/static/search/img/59bf3c47n91d65c73.jpg" alt="">a>
                                    <a href="/static/search/#" title="【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待">
                                        <em>华为 HUAWEI nova 2S 全面屏四摄 6GB +64GB 曜石黑 移动联通电信4G手机 双卡双待em>
                                    a>
                                    <div class="mc_price">
                                        <strong class="price">
                                            <span class="J-p-5963064">¥2999.00span>
                                        strong>
                                        <span class="mc-ico" title="购买本商品送赠品">
                                            <i class="goods-icons">赠品i>
                                        span>
                                    div>
                                    <div class="mc_rev">
                                        已有
                                        <a href="/static/search/#" class="number">12466a>
                                        人评价
                                    div>
                                li>
                                <li>
                                    <a href="/static/search/#" title="vivo X9s 全网通 4GB+64GB 磨砂黑 移动联通电信4G手机 双卡双待"><img src="/static/search/img/593ba628n8794c6a6.jpg" alt="">a>
                                    <a href="/static/search/#" title="【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待">
                                        <em>诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机em>
                                    a>
                                    <div class="mc_price">
                                        <strong class="price">
                                            <span class="J-p-5963064">¥1799.00span>
                                        strong>
                                        <span class="mc-ico" title="购买本商品送赠品">
                                            <i class="goods-icons">赠品i>
                                        span>
                                    div>
                                    <div class="mc_rev">
                                        已有
                                        <a href="/static/search/#" class="number">15600a>
                                        人评价
                                    div>
                                li>
                                <li>
                                    <a href="/static/search/#" title="vivo X9s 全网通 4GB+64GB 磨砂黑 移动联通电信4G手机 双卡双待"><img src="/static/search/img/5919637an271a1301.jpg" alt="">a>
                                    <a href="/static/search/#" title="【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待">
                                        <em>vivo Xplay6 全网通 6GB+64GB 磨砂黑 移动联通电信4G手机 双卡双待em>
                                    a>
                                    <div class="mc_price">
                                        <strong class="price">
                                            <span class="J-p-5963064">¥3498.00span>
                                        strong>
                                        <span class="mc-ico" title="购买本商品送赠品">
                                            <i class="goods-icons">赠品i>
                                        span>
                                    div>
                                    <div class="mc_rev">
                                        已有
                                        <a href="/static/search/#" class="number">5369a>
                                        人评价
                                    div>
                                li>
                            ul>
                        div>
                    div>
                    <div class="JD_con_one">
                        <div class="mt">
                            <h3>达人选购h3>
                        div>
                        <div class="mc">
                            <ul>
                                <li>
                                    <a href="/static/search/#" title="vivo X9s 全网通 4GB+64GB 磨砂黑 移动联通电信4G手机 双卡双待"><img src="/static/search/img/59bf3c47n91d65c73.jpg" alt="">a>
                                    <a href="/static/search/#">
                                        <em>华为 HUAWEI nova 2S 全面屏四摄 6GB +64GB 曜石黑 移动联通电信4G手机 双卡双待em>
                                    a>
                                    <div class="mc_price">
                                        <strong class="price">
                                            <span class="J-p-5963064">¥2999.00span>
                                        strong>
                                    div>
                                li>
                                <li>
                                    <a href="/static/search/#" title="vivo X9s 全网通 4GB+64GB 磨砂黑 移动联通电信4G手机 双卡双待"><img src="/static/search/img/59bf3c47n91d65c73.jpg" alt="">a>
                                    <a href="/static/search/#">
                                        <em>华为 HUAWEI nova 2S 全面屏四摄 6GB +64GB 曜石黑 移动联通电信4G手机 双卡双待em>
                                    a>
                                    <div class="mc_price">
                                        <strong class="price">
                                            <span class="J-p-5963064">¥2999.00span>
                                        strong>
                                    div>
                                li>
                                <li>
                                    <a href="/static/search/#" title="vivo X9s 全网通 4GB+64GB 磨砂黑 移动联通电信4G手机 双卡双待"><img src="/static/search/img/593ba628n8794c6a6.jpg" alt="">a>
                                    <a href="/static/search/#">
                                        <em>诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机em>
                                    a>
                                    <div class="mc_price">
                                        <strong class="price">
                                            <span class="J-p-5963064">¥1799.00span>
                                        strong>
                                    div>
                                li>
                                <li>
                                    <a href="/static/search/#" title="vivo X9s 全网通 4GB+64GB 磨砂黑 移动联通电信4G手机 双卡双待"><img src="/static/search/img/5919637an271a1301.jpg" alt="">a>
                                    <a href="/static/search/#">
                                        <em>vivo Xplay6 全网通 6GB+64GB 磨砂黑 移动联通电信4G手机 双卡双待em>
                                    a>
                                    <div class="mc_price">
                                        <strong class="price">
                                            <span class="J-p-5963064">¥3498.00span>
                                        strong>
                                    div>
                                li>
                            ul>
                        div>
                    div>
                    <div class="JD_con_one" style="border:none;">
                        <div class="mt">
                            <h3>商品精选h3>
                            <span>广告span>
                        div>
                        <div class="mc">
                            <ul>
                                <li>
                                    <a href="/static/search/#"><img src="/static/search/img/599a806bn9d829c1c.jpg" alt="">a>
                                li>
                                <li>
                                    <a href="/static/search/#"><img src="/static/search/img/593e4de0n5ff878a4.jpg" alt="">a>
                                li>
                            ul>
                        div>
                    div>
                div>
            div>
            
            <div class="JD_con_right">
                <div class="filter">
                    
                    <div class="filter_top">
                        <div class="filter_top_left">
                            <a href="/static/search/#">综合排序a>
                            <a href="/static/search/#">销量a>
                            <a href="/static/search/#">价格a>
                            <a href="/static/search/#">评论分a>
                            <a href="/static/search/#">上架时间a>
                        div>
                        <div class="filter_top_right">
                            <span class="fp-text">
                               <b>1b><em>/em><i>169i>
                           span>
                            <a href="/static/search/#" class="prev"><a>
                            <a href="/static/search/#" class="next"> > a>
                        div>
                    div>
                    
                    <div class="filter_bottom">
                        <div class="filter_bottom_left">
                            <div class="fs-cell">收货地div>
                            <div class="dizhi">
                                <div class="dizhi_show">
                                    <em>北京朝阳区三环以内em>
                                    <b>b>
                                div>
                            div>
                            <div class="dizhi_con">
                                <ul id="tab">
                                    <li id="tab1" value="1">北京 <img src="/static/search/image/[email protected]" alt="">li>
                                    <li id="tab2" value="2">朝阳 <img src="/static/search/image/[email protected]" alt="">li>
                                    <li id="tab3" value="3">三环以内 <img src="/static/search/image/[email protected]" alt="">li>
                                ul>
                                <div id="container">
                                    <div id="content1" style="z-index: 1;">
                                        <a href="/static/search/#">北京a>
                                        <a href="/static/search/#">上海a>
                                        <a href="/static/search/#">天津a>
                                        <a href="/static/search/#">重庆a>
                                        <a href="/static/search/#">河北a>
                                        <a href="/static/search/#">山西a>
                                        <a href="/static/search/#">河南a>
                                        <a href="/static/search/#">辽宁a>
                                        <a href="/static/search/#">吉林a>
                                        <a href="/static/search/#">黑龙江a>
                                        <a href="/static/search/#">内蒙古a>
                                        <a href="/static/search/#">江苏a>
                                        <a href="/static/search/#">山东a>
                                        <a href="/static/search/#">安徽a>
                                        <a href="/static/search/#">浙江a>
                                        <a href="/static/search/#">福建a>
                                        <a href="/static/search/#">湖北a>
                                        <a href="/static/search/#">湖南a>
                                        <a href="/static/search/#">广东a>
                                        <a href="/static/search/#">广西a>
                                        <a href="/static/search/#">江西a>
                                        <a href="/static/search/#">四川a>
                                        <a href="/static/search/#">海南a>
                                        <a href="/static/search/#">贵州a>
                                        <a href="/static/search/#">云南a>
                                        <a href="/static/search/#">西藏a>
                                        <a href="/static/search/#">陕西a>
                                        <a href="/static/search/#">甘肃a>
                                        <a href="/static/search/#">青海a>
                                        <a href="/static/search/#">宁夏a>
                                        <a href="/static/search/#">新疆a>
                                        <a href="/static/search/#">港澳a>
                                        <a href="/static/search/#">台湾a>
                                        <a href="/static/search/#">钓鱼岛a>
                                        <a href="/static/search/#">海外a>

                                    div>
                                    <div id="content2">
                                        <a href="/static/search/#">朝阳区a>
                                        <a href="/static/search/#">海淀区a>
                                        <a href="/static/search/#">西城区a>
                                        <a href="/static/search/#">东城区a>
                                        <a href="/static/search/#">大兴区a>
                                        <a href="/static/search/#">丰台区a>
                                        <a href="/static/search/#">昌平区a>
                                        <a href="/static/search/#">顺义区a>

                                    div>
                                    <div id="content3">
                                        <a href="/static/search/#">三环以内a>
                                        <a href="/static/search/#">管庄a>
                                        <a href="/static/search/#">北苑a>
                                        <a href="/static/search/#">定福庄a>
                                        <a href="/static/search/#">三环到四环之间a>
                                        <a href="/static/search/#">四环到五环之间a>
                                        <a href="/static/search/#">五环到六环之间a>
                                    div>
                                div>
                            div>
                        div>
                        <div class="filter_bottom_right">
                            <ul>
                                <li>
                                    <a href="/static/search/#">
                                        <i>i>
                                        谷粒商城配送
                                    a>
                                li>
                                <li>
                                    <a href="/static/search/#">
                                        <i>i>
                                        京尊达                                    a>
                                li>
                                <li>
                                    <a href="/static/search/#">
                                        <i>i>
                                        货到付款
                                    a>
                                li>
                                <li>
                                    <a href="/static/search/#">
                                        <i>i>
                                        仅显示有货
                                    a>
                                li>
                                <li>
                                    <a href="/static/search/#">
                                        <i>i>
                                        可配送全球
                                    a>
                                li>
                            ul>
                        div>

                    div>
                    
                    <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.00span>
                            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="filter_page">
                        <div class="page_wrap">
                            <span class="page_span1">
                                <a href="/static/search/#">
                                    < 上一页
                                a>
                                <a href="/static/search/#" style="border: 0;color:#ee2222;background: #fff">1a>
                                <a href="/static/search/#">2a>
                                <a href="/static/search/#">3a>
                                <a href="/static/search/#" style="border: 0;font-size: 20px;color: #999;background: #fff">...a>
                                <a href="/static/search/#">169a>
                                <a href="/static/search/#">
                                    下一页 >
                                a>
                            span>
                            <span class="page_span2">
                                <em><b>169b>  到第em>
                                <input type="number" value="1">
                                <em>em>
                                <a href="/static/search/#">确定a>
                            span>
                        div>
                    div>
                div>
            div>
        div>
    div>
div>

效果:



1.5.2、页面的筛选条件渲染


将结果的品牌、分类、商品属性进行遍历显示,并且点击某个属性值时可以通过拼接url进行跳转

  1. 在list页面中编写一个页面跳转方法
  • 分类
  • 品牌
  • 规格属性
<a href="/static/search/#" th:href="${'javascript:searchProducts("brandId",'+brand.brandId+')'}">
    <img th:src="${brand.brandImg}" alt="">
    <div th:text="${brand.brandName}">
        华为(HUAWEI)
    div>
a>
<li th:each="catalog:${result.ctatLogs}">
    <a href="/static/search/#"
       th:href="${'javascript:searchProducts("catalog3Id",'+catalog.catalogId+')'}"
       th:text="${catalog.catalogName}">分类a>
li>
<li th:each="val:${attr.attrValue}">
    <a href="/static/search/#"
       th:href="${'javascript:searchProducts("attrs","'+attr.attrId+'_'+val+'")'}"
       th:text="${val}">5.56英寸及以上a>
li>
function searchProducts(name,value){
    // 原来的页面
    var href = location.href + "";
    if (href.indexOf("?")!=-1) {
        location.href = location.href + "&"+name+"="+value;
    } else {
        location.href = location.href + "?"+name+"="+value;
    }
}

1.5.3、导航搜索功能


<div class="header_form">
    <input id="keyword_input" type="text" placeholder="手机" />
    <a href="javascript:searchByKeyword();">搜索a>
div>
function searchByKeyword() {
    searchProducts("keyword",$("#keyword_input").val());
}

之后做了修改:

function searchProducts(name,value){
location.href = replaceAndAddParamVal(location.href,"keyword",value);
}

1.5.4、页面分页数据渲染



<div class="filter_page">
    <div class="page_wrap">
        <span class="page_span1">
            <a class="page_a" th:attr="pn=${result.pageNum - 1}" th:if="${result.pageNum>1}">
                < 上一页
            a>
            <a class="page_a" th:attr="pn=${nav},style=${nav == result.pageNum?'border: 0;color:#ee2222;background: #fff':''}"
               th:each="nav:${result.pageNavs}">[[${nav}]]a>
            <a class="page_a" th:attr="pn=${result.pageNum + 1}" th:if="${result.pageNum">
                下一页 >
            a>
        span>
        <span class="page_span2">
            <em><b>[[${result.totalPages}]]b>  到第em>
            <input type="number" value="1">
            <em>em>
            <a class="page_submit" >确定a>
        span>
    div>
div>
$(".page_a").click(function () {
    var pn = $(this).attr("pn");
    var href = location.href;
    if (href.indexOf("pageNum")!=-1){
        // 替换pageNum的值
        location.href = replaceParamVal(href,"pageNum",pn);
    } else if (href.indexOf("?")!=-1){
        location.href = location.href + "&pageNum="+pn;
    } else {
        location.href = location.href + "?pageNum="+pn;
    }
    return false;
})

function replaceParamVal(url,paramName,replaceVal) {
    var oUrl = url.toString();
    var re=eval('/('+paramName+'=)([^&]*)/gi');
    var nUrl = oUrl.replace(re,paramName+"="+replaceVal);
    return nUrl;
}

1.5.5、页面排序功能


  • 页面排序功能需要保证,点击某个按钮时,样式会变红,并且其他的样式保持最初的样子;

  • 点击某个排序时首先按升序显示,再次点击再变为降序,并且还会显示上升或下降箭头

  • 页面排序跳转的思路是通过点击某个按钮时会向其class属性添加/去除desc,并根据属性值进行url拼接

<div class="filter_top">
    <div class="filter_top_left" th:with="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: #FFF;border-color:#e4393c;background:#e4393c'
           :'color: #333;border-color:#CCC;background:#FFF'}"
           sort="hotScore" 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: #FFF;border-color:#e4393c;background:#e4393c'
           :'color: #333;border-color:#CCC;background:#FFF'}"
           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: #FFF;border-color:#e4393c;background:#e4393c'
           :'color: #333;border-color:#CCC;background:#FFF'}"
           sort="skuPrice" href="/static/search/#">
            价格[[${(!#strings.isEmpty(p) && #strings.startsWith(p,'saleCount') && #strings.endsWith(p,'desc'))?'↓':'↑'}]]
        a>
        <a href="/static/search/#">评论分a>
        <a href="/static/search/#">上架时间a>
    div>
    <div class="filter_top_right">
        <span class="fp-text">
           <b>1b><em>/em><i>169i>
       span>
        <a href="/static/search/#" class="prev"><a>
        <a href="/static/search/#" class="next"> > a>
    div>
div>
function replaceAndAddParamVal(url,paramName,replaceVal) {
  var oUrl = url.toString();
  // 1、如果没有就添加,有就替换
  if (oUrl.indexOf(paramName)!=-1){
    var re=eval('/('+paramName+'=)([^&]*)/gi');
    var nUrl = oUrl.replace(re,paramName+"="+replaceVal);
    return nUrl;
  }else{
    var nUrl = "";
    if (oUrl.indexOf("?")!=-1) {
      nUrl = oUrl + "&" + paramName+"="+replaceVal;
    } else {
      nUrl = oUrl + "?" + paramName+"="+replaceVal;
    }
  }
  return nUrl;
}
$(".sort_a").click(function () {
    // 1、改变当前元素以及兄弟元素的样式
    // changeStyle(this);
    $(this).toggleClass("desc");
    // 2、跳转到指定位置
    var sort = $(this).attr("sort");
    sort = $(this).hasClass("desc")?sort+"_desc":sort+"_asc";
    location.href = replaceAndAddParamVal(location.href,"sort",sort);

    // 禁用默认行为
    return false;
});

1.5.6、页面价格区间搜索

<a href="/static/search/#">评论分a>
<a href="/static/search/#">上架时间a>
<input id="skuPriceFrom" type="number" th:value="${#strings.isEmpty(priceRange)?'':#strings.substringBefore(priceRange,'_')}" style="width: 100px; margin-left: 30px;"/> -
<input id="skuPriceTo" type="number" th:value="${#strings.isEmpty(priceRange)?'':#strings.substringAfter(priceRange,'_')}" style="width: 100px;"/>
<button id="skuPriceSearchBtn">确定button>

价格区间搜索函数:

$("#skuPriceSearchBtn").click(function () {
    // 1、拼上价格区间的查询条件
    var from = $("#skuPriceFrom").val();
    var to = $("#skuPriceTo").val();

    var query = from + "_" + to;

    location.href = replaceAndAddParamVal(location.href,"skuPrice",query);
});

1.5.7、是否只显示有库存


<li>
    <a href="#" th:with="check = ${param.hasStock}">
        <input id="showHasStock" type="checkbox" th:checked="${#strings.equals(check,'1')}" >
        仅显示有货
    a>
li>
$("#showHasStock").change(function () {
    if ($(this).prop('checked')){
        location.href = replaceAndAddParamVal(location.href,"hasStock",1);
    }else{
        // 没选中
        var re = eval('/(&hasStock=)([^&]*)/gi');
        location.href = (location.href+"").replace(re,'');
    }
    return false;
});

1.5.8、面包屑导航


1.5.8.1、后端接口编写

第一步、编写远程调用gulimall-product服务的方法

  1. 定义springcloud的版本

    1.8
    7.4.2
    Greenwich.SR3

  1. 添加依赖管理
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-dependenciesartifactId>
            <version>${spring-cloud.version}version>
            <type>pomtype>
            <scope>importscope>
        dependency>
    dependencies>
dependencyManagement>
  1. 添加依赖
<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
  1. 修改gulimall-product中info方法Serice层实现类方法,加入缓存
@Cacheable(value = "attr",key = "'attrinfo:'+#root.args[0]")
@Override
public AttrRespVo getAttrInfo(Long attrId) {
    AttrRespVo respVo = new AttrRespVo();
    AttrEntity attrEntity = this.getById(attrId);
  	//.....
}
  1. 编写接口
@FeignClient("gulimall-product")
public interface ProductFeignService {

    @GetMapping("/product/attr/info/{attrId}")
    public R attrInfo(@PathVariable("attrId") Long attrId);
}

第二步、修改检索返回信息类VO

在 SearchResult 实体类中加入面包屑导航数据

    // 面包屑导航数据
    private List<NavVO> navs;
    @Data
    public static class NavVO{
        private String navName;
        private String navValue;
        private String link;
    }

第三步、修改com.atguigu.common.utils.R类,代码如下

public <T> T getData(String key,TypeReference<T> typeReference){
   Object data = get(key);    // 默认是map
   String s = JSON.toJSONString(data);
   T t = JSON.parseObject(s, typeReference);
   return t;
}

第四步、创建一个AttrResponseVo类,接收远程查询过来的数据

@Data
public class AttrResponseVo {
    /**
     * 属性id
     */
    private Long attrId;
    /**
     * 属性名
     */
    private String attrName;
    /**
     * 是否需要检索[0-不需要,1-需要]
     */
    private Integer searchType;
    /**
     * 值类型[0-为单个值,1-可以选择多个值]
     */
    private Integer valueType;
    /**
     * 属性图标
     */
    private String icon;
    /**
     * 可选值列表[用逗号分隔]
     */
    private String valueSelect;
    /**
     * 属性类型[0-销售属性,1-基本属性,2-既是销售属性又是基本属性]
     */
    private Integer attrType;
    /**
     * 启用状态[0 - 禁用,1 - 启用]
     */
    private Long enable;
    /**
     * 所属分类
     */
    private Long catelogId;
    /**
     * 快速展示【是否展示在介绍上;0-否 1-是】,在sku中仍然可以调整
     */
    private Integer showDesc;

    private Long attrGroupId;

    /**
     *           "catelogName": "手机/数码/手机", //所属分类名字
     *           "groupName": "主体", //所属分组名字
     */
    private String catelogName;
    private String groupName;

    private Long[] catelogPath;
}

第五步、修改Controller类

@GetMapping("/list.html")
public String listPage(SearchParam param, Model model, HttpServletRequest request){

    param.set_queryString(request.getQueryString());
    // 1、根据传递过来的页面的查询参数,去es中检索商品
    SearchResult result = mallSearchService.search(param);
    model.addAttribute("result",result);
    return "list";
}

第六步、Searvice实现类方法修改

MallSearchServiceImpl实现类的buildSearchResult方法下:

if (param.getAttrs()!=null && param.getAttrs().size()>0 ) {
  List<SearchResult.NavVO> navVOs = param.getAttrs().stream().map(attr -> {
    // 1、分析每一个attrs传过来的查询参数值
    SearchResult.NavVO navVO = new SearchResult.NavVO();
    String[] s = attr.split("_");
    navVO.setNavValue(s[1]);
    R r = productFeignService.attrInfo(Long.parseLong(s[0]));
    if (r.getCode()==0) {
      AttrResponseVo data = r.getData("attr", new TypeReference<AttrResponseVo>() {
      });
      navVO.setNavName(data.getAttrName());
    } else {
      navVO.setNavName(s[0]);
    }
    // 2、取消了这个面包屑以后,我们要跳转到哪个地方。将请求地址的url里面的当前条件置空
    // 拿到所有的查询条件,去掉当前。
    String encode = null;
    try {
      encode = URLEncoder.encode(attr, "UTF-8");
      encode = encode.replace("+", "%20"); // 浏览器对空格的编码和Java不一样
    } catch (UnsupportedEncodingException e) {
      e.printStackTrace();
    }
    String replace = param.get_queryString().replace("&attrs=" + encode, "");
    navVO.setLink("http://search.gulimall.cn/list.html?"+replace);
    return navVO;
  }).collect(Collectors.toList());
  result.setNavs(navVOs);
}
1.5.8.2、前端渲染


<div class="JD_ipone_one c">
    <a th:href="${nav.link}" th:each="nav:${result.navs}">
      <span th:text="${nav.navName}">span>
      <span>span>
      <span th:text="${nav.navValue}">span>
      x
    a>
div>
1.5.8.3、面包屑导航【条件筛选联动】

1.5.8.3.1、gulimall-product服务查询品牌接口

第一步、编写Controller层方法

@GetMapping("/infos")
public R info(@RequestParam("brandIds") List<Long> brandIds){
    List<BrandEntity> brand = brandService.getBrandsByIds(brandIds);

    return R.ok().put("brand", brand);
}

第二步、Service层实现类编写

@Override
public List<BrandEntity> getBrandsByIds(List<Long> brandIds) {
    return baseMapper.selectList(new QueryWrapper<BrandEntity>().in("brandId", brandIds));
}

第三步、gulimall-search服务中编写Feign接口

@FeignClient("gulimall-product")
public interface ProductFeignService {

    @GetMapping("/product/brand/infos")
    public R brandsInfo(@RequestParam("brand_id") List<Long> brandIds);
}
1.5.8.3.2、接口实现类编写

MallSearchServiceImpl 实现类的中的buildSearchResult方法

// 7、品排、分类
if (param.getBrandId()!=null && param.getBrandId().size()>0) {
    List<SearchResult.NavVO> navs = result.getNavs();
    SearchResult.NavVO navVO = new SearchResult.NavVO();
    navVO.setNavName("品牌");
    // TODO 远程查询所有品牌
    R r = productFeignService.brandsInfo(param.getBrandId());
    if (r.getCode()==0) {
        List<BrandVo> brand = r.getData("brand", new TypeReference<List<BrandVo>>() {
        });
        StringBuffer buffer = new StringBuffer();
        String replace = "";
        for (BrandVo brandVo : brand) {
            buffer.append(brandVo.getBrandName()+";");
            replace = replaceQueryString(param, brandVo.getBrandId()+"" ,"brandId");
        }
        navVO.setNavValue(buffer.toString());
        navVO.setLink("http://search.gulimall.cn/list.html?"+replace);
    }
    navs.add(navVO);
}
private String replaceQueryString(SearchParam param, String value,String key) {
  String encode = null;
  try {
    encode = URLEncoder.encode(value, "UTF-8");
    encode = encode.replace("+", "%20"); // 浏览器对空格的编码和Java不一样
  } catch (UnsupportedEncodingException e) {
    e.printStackTrace();
  }
  String replace = param.get_queryString().replace("&"+key+"=" + encode, "");
  return replace;
}
1.5.8.3.3、前端页面渲染

<div class="JD_nav_logo" th:with="brandid = ${param.brandId}">
    
    <div th:if="${#strings.isEmpty(brandid)}" 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/#" th:href="${'javascript:searchProducts("brandId",'+brand.brandId+')'}">
                            <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.ctatLogs}">
                    <a href="/static/search/#"
                       th:href="${'javascript:searchProducts("catalog3Id",'+catalog.catalogId+')'}"
                       th:text="${catalog.catalogName}">分类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}" th:if="${!#lists.contains(result.attrIds,attr.attrId)}">
        <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:href="${'javascript:searchProducts("attrs","'+attr.attrId+'_'+val+'")'}"
                       th:text="${val}">5.56英寸及以上a>
                li>
            ul>
        div>
    div>
div>

你可能感兴趣的:(谷粒商城,elasticsearch,教育电商,前端,后端)