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_routeuri: lb://gulimall-productpredicates:- Host=gulimall.cn- id: gulimall_search_routeuri: lb://gulimall-searchpredicates:- 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;         // 当前查询到的结果,所有涉及到的属性//==================以上返回给页面的所有信息====================/*** 品牌*/@Datapublic static class BrandVo{private Long brandId;private String brandName;private String brandImg;}/*** 分类*/@Datapublic static class CatalogVo{private Long catalogId;private String catalogName;}/*** 属性*/@Datapublic 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 {@Autowiredprivate RestHighLevelClient client;@Overridepublic 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-queryBoolQueryBuilder 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];   // 检索的属性idString[] attrValues = s[1].split(":");  // 这个属性检索用的值nestedBoolBuilder.must(QueryBuilders.termQuery("attrs.attrId",attrId));nestedBoolBuilder.must(QueryBuilders.termsQuery("attrs.attrValue",attrValues));// 每一个必须都得生成一个嵌入式的查询nestedNestedQueryBuilder 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/ascString[] 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、聚合brandsourceBuilder.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、聚合catalogsourceBuilder.aggregation(catalog_agg);// 5.3、属性聚合NestedAggregationBuilder attrs_agg = AggregationBuilders.nested("attrs_agg", "attrs"); // 嵌入聚合// 聚合出当前所有的attrIdTermsAggregationBuilder 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对应所有可能的属性值attrValueattr_id_agg.subAggregation(AggregationBuilders.terms("attr_value_agg").field("attrs.attrValue").size(50));attrs_agg.subAggregation(attr_id_agg);// TODO 3、聚合attrsourceBuilder.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、得到属性的idlong 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、得到品牌的idlong brandId = bucket.getKeyAsNumber().longValue();// 2、得到品牌的nameString brandName = ((ParsedStringTerms) bucket.getAggregations().get("brand_name_agg")).getBuckets().get(0).getKeyAsString();// 3、得到品牌的imgString 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();// 得到分类idcatalogVo.setCatalogId(bucket.getKeyAsNumber().longValue());// 得到分类nameParsedStringTerms 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;@Datapublic 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);    // 默认是mapString 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>

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

  1. 谷粒商城-分布式高级篇【业务编写】

    谷粒商城-分布式基础篇[环境准备] 谷粒商城-分布式基础[业务编写] 谷粒商城-分布式高级篇[业务编写]持续更新 谷粒商城-分布式高级篇-ElasticSearch 谷粒商城-分布式高级篇-分布式锁与 ...

  2. 谷粒商城-分布式高级篇[商城业务-秒杀服务]

    谷粒商城-分布式基础篇[环境准备] 谷粒商城-分布式基础[业务编写] 谷粒商城-分布式高级篇[业务编写]持续更新 谷粒商城-分布式高级篇-ElasticSearch 谷粒商城-分布式高级篇-分布式锁与 ...

  3. 谷粒商城分布式高级篇(中)

    谷粒商城分布式基础篇 谷粒商城分布式高级篇(上) 谷粒商城分布式高级篇(中) 谷粒商城分布式高级篇(下) 文章目录 商城业务 异步 异步复习 线程池详解 CompletableFuture Compl ...

  4. 谷粒商城分布式高级篇学习笔记

    ElasticSearch Feign调用流程 SynchronousMethodHandler.java的invoke()方法 1.构造请求数据,将对象转为json SynchronousMetho ...

  5. 谷粒商城分布式高级篇

    ElasticSearch 商品发布代码 es索引的设计 (1)方便检索{ skuId:1 spuId:1 skuTitle:华为xx price:9988 saleCount:99 attrs:[ ...

  6. 谷粒商城分布式高级篇总结文档

    目录 Elasticsearch Docker安装Elaticsearch(简称:ES) Docker安装Kibana (ES可视化界面) 启动Kibana遇到的坑 安装htop 如何删除卸载dock ...

  7. 谷粒商城电商项目 分布式高级篇

    更多视频,JAVA收徒 QQ:987115885谷粒商城电商项目 分布式高级篇102.全文检索-ElasticSearch-简介.mp4103.全文检索-ElasticSearch-Docker安装E ...

  8. 谷粒商城--认证中心--高级篇笔记八

    谷粒商城–认证中心–高级篇笔记八 1. 环境搭建 1.1 新建模块gulimall-auth-server 1.2 pom文件 上面没选好直接复制下面的pom文件,记得排除gulimall-commo ...

  9. 谷粒商城-分布式基础篇-环境搭建

    1.写在前面 既个人博客系统和Java虚拟机学习后,深感技术点过于零散,于是照着尚硅谷教程写了谷粒商城这个项目.谷粒商城是一个完整的大型分布式架构电商平台,这个项目将我目前学到的知识点,以及还未学到的 ...

最新文章

  1. API 面试四连杀:接口如何设计?安全如何保证?签名如何实现?防重如何实现?...
  2. 独家 | 6种让Python程序变慢的坏习惯
  3. 器官复刻、脑机接口、电子皮肤…这些前沿科学或改写人类未来
  4. Yann LeCun专访:我不觉得自己有天分,但是我一直往聪明人堆里钻
  5. 不是计算机专业学python能找到工作吗-本科学历,非计算机专业,想学Python找工作靠谱吗?...
  6. undefined symbol: sqlite3_open_v2
  7. DES和RSA算法的java实现
  8. stm32怎么调用for循环内部的变量_循环中的异步amp;amp;循环中的闭包
  9. dedecms最新版本修改任意管理员漏洞
  10. leetcode994. 腐烂的橘子(bfs)
  11. 用libconfig读取配置文件
  12. 【linux】xx is not in the sudoers file 解决办法
  13. 模板 - 数学 - 数论 - 扩展欧几里得算法
  14. Python中文分词神器---jieba
  15. 进销存mysql数据库_进销存管理系统数据库设计
  16. 2021年最值得推荐的10款数据可视化工具,看完我收藏了
  17. 安卓手机 wifi 探针
  18. Punti特征码定位器(原SignatureTest) 2022 Q1V1
  19. 基于51单片机的篮球记分牌设计
  20. 启明创投邝子平谈禾赛上市:做硬科技领域长线投资人

热门文章

  1. 永恒之蓝ms17_010漏洞复现
  2. Apache Griffin+Flink+Kafka实现流式数据质量监控实战
  3. Win10自带的录屏功能怎么用?
  4. 【图像修复】基于深度学习的图像修复算法的MATLAB仿真
  5. L48.linux命令每日一练 -- 第七章 Linux用户管理及用户信息查询命令 -- last、lastb和lastlog
  6. 记一次confluence邮件服务器配置的坑(阿里企业邮箱)
  7. 最受中国人欢迎的10种日本药
  8. Crtl+Enter提交留言
  9. H-divergence
  10. 2.SSM之Spring整合、AOP及Spring事务