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

2023-11-12

  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.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
    </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. 修改本地的 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 = $("<a href=\"http://search.gulimall.cn/list.html?catalog3Id="+ctg3.id+"\" style=\"color: #999;\">" + ctg3.name + "</a>");
    
  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 = $("<a href=\"http://search.gulimall.cn/list.html?catalog3Id="+ctg3.id+"\" style=\"color: #999;\">" + ctg3.name + "</a>");
    
  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)、输入检索关键字展示检索页
    在这里插入图片描述

  • 3)、选择筛选条件进入
    在这里插入图片描述


  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": "<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
          }
        }
      }
    },
    "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("<b style='color:red'>");
        highlightBuilder.postTags("</b>");
        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 class="rig_tab> 只留下一个div
    • 删掉 <div class="rig_tab> 盒子下的三个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">&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.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.00</span>
                                        </strong>
                                        <span class="mc-ico" title="购买本商品送赠品">
                                            <i class="goods-icons">赠品</i>
                                        </span>
                                    </div>
                                    <div class="mc_rev">
                                        已有
                                        <a href="/static/search/#" class="number">12466</a>
                                        人评价
                                    </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.00</span>
                                        </strong>
                                        <span class="mc-ico" title="购买本商品送赠品">
                                            <i class="goods-icons">赠品</i>
                                        </span>
                                    </div>
                                    <div class="mc_rev">
                                        已有
                                        <a href="/static/search/#" class="number">12466</a>
                                        人评价
                                    </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.00</span>
                                        </strong>
                                        <span class="mc-ico" title="购买本商品送赠品">
                                            <i class="goods-icons">赠品</i>
                                        </span>
                                    </div>
                                    <div class="mc_rev">
                                        已有
                                        <a href="/static/search/#" class="number">15600</a>
                                        人评价
                                    </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.00</span>
                                        </strong>
                                        <span class="mc-ico" title="购买本商品送赠品">
                                            <i class="goods-icons">赠品</i>
                                        </span>
                                    </div>
                                    <div class="mc_rev">
                                        已有
                                        <a href="/static/search/#" class="number">5369</a>
                                        人评价
                                    </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.00</span>
                                        </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.00</span>
                                        </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.00</span>
                                        </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.00</span>
                                        </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>1</b><em>/</em><i>169</i>
                           </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/down-@1x.png" alt=""></li>
                                    <li id="tab2" value="2">朝阳 <img src="/static/search/image/down-@1x.png" alt=""></li>
                                    <li id="tab3" value="3">三环以内 <img src="/static/search/image/down-@1x.png" 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.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="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">1</a>
                                <a href="/static/search/#">2</a>
                                <a href="/static/search/#">3</a>
                                <a href="/static/search/#" style="border: 0;font-size: 20px;color: #999;background: #fff">...</a>
                                <a href="/static/search/#">169</a>
                                <a href="/static/search/#">
                                    下一页 >
                                </a>
                            </span>
                            <span class="page_span2">
                                <em><b>169</b>&nbsp;&nbsp;到第</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(&quot;brandId&quot;,'+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(&quot;catalog3Id&quot;,'+catalog.catalogId+')'}"
       th:text="${catalog.catalogName}">分类</a>
</li>
<li th:each="val:${attr.attrValue}">
    <a href="/static/search/#"
       th:href="${'javascript:searchProducts(&quot;attrs&quot;,&quot;'+attr.attrId+'_'+val+'&quot;)'}"
       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<result.totalPages}">
                下一页 >
            </a>
        </span>
        <span class="page_span2">
            <em><b>[[${result.totalPages}]]</b>&nbsp;&nbsp;到第</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>1</b><em>/</em><i>169</i>
       </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的版本
<properties>
    <java.version>1.8</java.version>
    <elasticsearch.version>7.4.2</elasticsearch.version>
    <spring-cloud.version>Greenwich.SR3</spring-cloud.version>
</properties>
  1. 添加依赖管理
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
  1. 添加依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</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(&quot;brandId&quot;,'+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(&quot;catalog3Id&quot;,'+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(&quot;attrs&quot;,&quot;'+attr.attrId+'_'+val+'&quot;)'}"
                       th:text="${val}">5.56英寸及以上</a>
                </li>
            </ul>
        </div>
    </div>
</div>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

谷粒商城-分布式高级篇[商城业务-检索服务] 的相关文章

随机推荐

  • 各种分布式文件系统简介

    常见的分布式文件系统有 GFS HDFS Lustre Ceph GridFS mogileFS TFS FastDFS等 各自适用于不同的领域 它们都不是系统级的分布式文件系统 而是应用级的分布式文件存储服务 Google学术论文 这是众
  • java判断微信号是否关注微信公众号

    public CommonResult validateAttentionWxPublic String openId throws Exception CommonResult cr new CommonResult String acc
  • [event] Embedded Linux Conference 2016

    本文转载至 http events linuxfoundation org events embedded linux conference 转载说明 做嵌入式Linux开发的最好都看一下 今年的主题很大一块都是IoT相关 另外可以参考 h
  • 微信小程序的下载安装

    微 信 小 程 序 color purple 微信小程序 微信小程序 微信小程序 简称
  • for循环三种跳出循环的方法(retrun、continue、break)

    continue 指的是跳出当前循环 即不执行continue后的语句 直接进入下次循环 break 指的是跳出for本身 不再进行之后的循环 但可以执行for循环之外的语句 return 指的是跳出for循环 且不执行for循环之外的语句
  • 我用ChatGPT写2023高考语文作文(七):上海卷

    2023年 上海卷 适用地区 上海 一个人乐意去探索陌生世界 仅仅是因为好奇心吗 请写一篇文章 谈谈你对这个问题的认识和思考 要求 1 自拟题目 2 不少于 800字 文章目录 探索陌生世界的动力 好奇心与更多 好奇心无疑是人类探索陌生世界
  • RFID无人机之智能仓储管理系统应用

    随着直播 短视频 真人秀等节目的蓬勃发展 应用无人安全驾驶航天器 UAV 的项目数不胜数 于前不久新华社还宣布组建无人 机新闻采编队伍 伴随着世界电子物联网技术的发展 民用无人 机的花样用法更是层出不穷 文章主要讲基于RFID无人机智能管理
  • python范围无穷_Python – 输入包含NaN,无穷大或对于dtype(‘float64’)来说太大的值...

    我是Python的新手 我正在尝试使用sklearn cluster 这是我的代码 from sklearn cluster import MiniBatchKMeans kmeans MiniBatchKMeans n clusters
  • 用C语言输出各种三角形

    用C语言输出各种三角形 三角形类型 用C语言输出各种三角形 1 直角在左下角的三角形 2 直角在左上角的三角形 3 直角在右下角的三角形 4 直角在右上角的三角形 5 正三角形 金字塔 倒三角形 1 直角在左下角的三角形 代码 includ
  • Python使用selenium设置无浏览器(界面)运行

    设置无界面 浏览器 运行代码 from selenium import webdriver from selenium webdriver import ChromeOptions from selenium webdriver suppo
  • HDR dump失败解决办法

    运行脚本 进HDR拍照 一般在 sdcard Android data com oplus camera files spdebug hdrdump 目录下 可以生成dump 若没有生成dump 清除相机缓存 重新运行脚本 重启手机即可 脚
  • Apache Solr入门教程(初学者之旅)

    Apache Solr入门教程 初学者之旅 写在前面 本文涉及solr入门的各方面 建议边思考边实践 相信能帮助你对solr有个清晰全面的了解并能简单实用 在Apache Solr初学者教程的这个例子中 我们将讨论有关如何安装最新版本的Ap
  • 0-1分布的方差和期望

    最后欢迎大家访问我的个人网站 1024s
  • CentOS7.4下C++开源日志库easyloggingpp的使用

    CentOS7 4下C 开源日志库easyloggingpp的使用 一 简单示例 二 多线程支持 Linux后台开发过程中经常需要日志记录一些运行信息 网上找到easyloggingpp只需要包含头文件和实现文件即可 使用很方便 现整理如下
  • vue:结合elementUI设计网站登录页

    这次主要是记录三个重点 1 组件间通信的方法 其一 2 脚手架搭建的vue工程的组件调用 3 elementUI 的轮播图与模态框的设计 先看效果图 简单的就做了这四个页面 总共四个组件实现这些效果 这个小网站需要的组件也挺多的 当然不止这
  • Pandas——读/写不同数据源的数据

    Pandas 读 写不同数据源的数据 一 读 写数据库数据 1 SQLAlchemy连接MySQL数据库 2 使用 read sql query read sql table read sql 函数 读 取数据库数据 3 使用 to sql
  • 全面总结机器学习超参数调优(附代码)

    公众号 尤而小屋作者 Peter编辑 Peter 大家好 我是Peter 本文的主题 机器学习建模的超参数调优 开局一张图 文章很长 建议直接收藏 一 什么是机器学习超参数 机器学习超参数是在开始学习过程之前设置值的参数 而不是通过训练得到
  • 导入elementui组件库

    1 在终端输入 vue add element 回车 选择按需加载 输入yes 回车 选择zh CN 回车 2 在plugins里面有一个element js 我们先去elementui官网看到快速上手 就可以看到按需引入 复制代码放到el
  • 搭建 react+ts+less+Antd 项目

    搭建 react ts less Antd 项目 一 文章目录 搭建 react ts less Antd 项目 一 前言 项目搭建流程 1 新建项目 2 暴露配置 3 支持less 4 支持ts 前言 项目搭建流程 1 新建项目 npx
  • 谷粒商城-分布式高级篇[商城业务-检索服务]

    谷粒商城 分布式基础篇 环境准备 谷粒商城 分布式基础 业务编写 谷粒商城 分布式高级篇 业务编写 持续更新 谷粒商城 分布式高级篇 ElasticSearch 谷粒商城 分布式高级篇 分布式锁与缓存 项目托管于gitee 一 商城业务 检