分布式高级篇
![在这里插入图片描述](https://img-blog.csdnimg.cn/2d2f2b0ef4e64997a4460dd9ef7ec561.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAdW5pcXVlX3BlcmZlY3Q=,size_15,color_FFFFFF,t_70,g_se,x_16#pic_center)
1 检索
检索参数VO与url
创建SearchParam用于检索VO
全文检索:skuTitle-》keyword
排序:saleCount(销量)、hotScore(热度分)、skuPrice(价格)
过滤:hasStock、skuPrice区间、brandId、catalog3Id、attrs
聚合:attrs
keyword=小米&
sort=saleCount_desc/asc&
hasStock=0/1&
skuPrice=400_1900&
brandId=1&
catalog3Id=1&
attrs=1_3G:4G:5G&
attrs=2_骁龙845&
attrs=4_高清屏
package com.yxj.gulimall.search.vo;
import lombok.Data;
import java.util.List;
@Data
public class SearchParam {
private String keyword;
private Long catalog3Id;
private String sort;
private Integer hasStock;
private String skuPrice;
private List<Long> brandId;
private List<String> attrs;
private Integer pageNum = 1;
private String _queryString;
}
检索结果VO
查询得到商品、总记录数、总页码
品牌list用于在品牌栏显示,分类list用于在分类栏显示
其他栏每栏用AttrVo表示
不仅要根据关键字从es中检索到商品
还要通过聚合生成品牌等信息,方便分类栏显示
package com.yxj.gulimall.search.vo;
import com.yxj.common.to.es.SkuEsModel;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
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<AttrVo> attrs;
private List<CatalogVo> catalogs;
private List<NavVo> navs = new ArrayList<>();
private List<Long> attrIds = new ArrayList<>();
@Data
public static class NavVo {
private String navName;
private String navValue;
private String link;
}
@Data
public static class BrandVo {
private Long brandId;
private String brandName;
private String brandImg;
}
@Data
public static class AttrVo {
private Long attrId;
private String attrName;
private List<String> attrValue;
}
@Data
public static class CatalogVo {
private Long catalogId;
private String catalogName;
}
}
package com.yxj.gulimall.search.service.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.yxj.common.to.es.SkuEsModel;
import com.yxj.common.utils.R;
import com.yxj.gulimall.search.config.GulimallElasticSearchConfig;
import com.yxj.gulimall.search.constant.EsConstant;
import com.yxj.gulimall.search.feign.ProductFeignService;
import com.yxj.gulimall.search.service.MallSearchService;
import com.yxj.gulimall.search.vo.AttrResponseVo;
import com.yxj.gulimall.search.vo.BrandVo;
import com.yxj.gulimall.search.vo.SearchParam;
import com.yxj.gulimall.search.vo.SearchResult;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@Service
public class MallSearchServiceImpl implements MallSearchService {
@Autowired
private RestHighLevelClient client;
@Autowired
private ProductFeignService productFeignService;
@Override
public SearchResult search(SearchParam param) {
SearchResult result = null;
SearchRequest searchRequest = buildSearchRequest(param);
try {
SearchResponse response = client.search(searchRequest, GulimallElasticSearchConfig.COMMON_OPTIONS);
result = buildSearchResult(response,param);
System.out.println(result.toString());
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
private SearchResult buildSearchResult(SearchResponse response,SearchParam param) {
SearchResult result = new SearchResult();
SearchHits hits = response.getHits();
List<SkuEsModel> list = 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);
}
list.add(esModel);
}
}
result.setProducts(list);
List<SearchResult.AttrVo> attrVos = new ArrayList<>();
ParsedNested attr_agg = response.getAggregations().get("attr_agg");
ParsedLongTerms attr_id_agg = attr_agg.getAggregations().get("attr_id_agg");
for (Terms.Bucket bucket : attr_id_agg.getBuckets()) {
SearchResult.AttrVo attrVo = new SearchResult.AttrVo();
long attrId = bucket.getKeyAsNumber().longValue();
attrVo.setAttrId(attrId);
String attrName = ((ParsedStringTerms) bucket.getAggregations().get("attr_name_agg")).getBuckets().get(0).getKeyAsString();
attrVo.setAttrName(attrName);
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.setAttrValue(attrValues);
attrVos.add(attrVo);
}
result.setAttrs(attrVos);
List<SearchResult.BrandVo> brands = new ArrayList<>();
ParsedLongTerms brand_agg = response.getAggregations().get("brand_agg");
List<? extends Terms.Bucket> brandAggBuckets = brand_agg.getBuckets();
for (Terms.Bucket bucket : brandAggBuckets) {
SearchResult.BrandVo brandVo = new SearchResult.BrandVo();
long brandId = bucket.getKeyAsNumber().longValue();
brandVo.setBrandId(brandId);
ParsedStringTerms brand_name_agg = bucket.getAggregations().get("brand_name_agg");
String brandName = brand_name_agg.getBuckets().get(0).getKeyAsString();
brandVo.setBrandName(brandName);
ParsedStringTerms brand_img_agg = bucket.getAggregations().get("brand_img_agg");
String brandImg = brand_img_agg.getBuckets().get(0).getKeyAsString();
brandVo.setBrandImg(brandImg);
brands.add(brandVo);
}
result.setBrands(brands);
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();
String keyAsString = bucket.getKeyAsString();
catalogVo.setCatalogId(Long.parseLong(keyAsString));
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.setCatalogs(catalogVos);
result.setPageNum(param.getPageNum());
long total = hits.getTotalHits().value;
result.setTotal(total);
int totalPages = 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);
if (param.getAttrs() !=null && param.getAttrs().size()>0){
List<SearchResult.NavVo> collect = param.getAttrs().stream().map(attr -> {
SearchResult.NavVo navVo = new SearchResult.NavVo();
String[] s = attr.split("_");
navVo.setNavValue(s[1]);
R r = productFeignService.attrInfo(Long.parseLong(s[0]));
result.getAttrIds().add(Long.parseLong(s[0]));
if (r.getCode() == 0){
AttrResponseVo attrResponseVo = (AttrResponseVo) r.getData("attr", new TypeReference<AttrResponseVo>() {
});
navVo.setNavName(attrResponseVo.getAttrName());
}else{
navVo.setNavName(s[0]);
}
String replace = replaceQueryString(param, attr,"attrs");
navVo.setLink("http://search.gulimail.com/list.html?"+replace);
return navVo;
}).collect(Collectors.toList());
result.setNavs(collect);
}
if (param.getBrandId() != null && param.getBrandId().size() >0){
List<SearchResult.NavVo> navs = result.getNavs();
SearchResult.NavVo navVo = new SearchResult.NavVo();
navVo.setNavName("品牌");
R r = productFeignService.brandsInfo(param.getBrandId());
if (r.getCode() == 0){
List<BrandVo> brand = (List<BrandVo>) 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.gulimail.com/list.html?"+replace);
}
navs.add(navVo);
result.setNavs(navs);
}
return result;
}
private String replaceQueryString(SearchParam param, String value, String key) {
String encode = null;
try {
encode = URLEncoder.encode(value, "UTF-8");
encode = encode.replace("+", "%20");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
String replace = param.get_queryString().replace("&"+key+"=" + encode, "");
return replace;
}
private SearchRequest buildSearchRequest(SearchParam param) {
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
if (!StringUtils.isEmpty(param.getKeyword())){
boolQuery.must(QueryBuilders.matchQuery("skuTitle",param.getKeyword()));
}
if (param.getCatalog3Id() != null){
boolQuery.filter(QueryBuilders.termQuery("catalogId", param.getCatalog3Id()));
}
if (param.getBrandId() != null && param.getBrandId().size()>0){
boolQuery.filter(QueryBuilders.termsQuery("brandId",param.getBrandId()));
}
if (param.getAttrs() != null && param.getAttrs().size()>0){
for (String attr : param.getAttrs()) {
BoolQueryBuilder nestedboolQuery = QueryBuilders.boolQuery();
String[] s = attr.split("_");
String attrId = s[0];
String[] attrValues = s[1].split(":");
nestedboolQuery.must(QueryBuilders.termQuery("attrs.attrId",attrId));
nestedboolQuery.must(QueryBuilders.termsQuery("attrs.attrValue",attrValues));
NestedQueryBuilder nestedQuery = QueryBuilders.nestedQuery("attrs", nestedboolQuery, ScoreMode.None);
boolQuery.filter(nestedQuery);
}
}
if (param.getHasStock() != null){
boolQuery.filter(QueryBuilders.termQuery("hasStock",param.getHasStock()==1));
}
if (!StringUtils.isEmpty(param.getSkuPrice())){
RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("skuPrice");
String[] s = param.getSkuPrice().split("_");
if (s.length == 2) {
rangeQuery.gte(s[0]).lte(s[1]);
} else if (s.length == 1) {
if (param.getSkuPrice().startsWith("_")){
rangeQuery.lte(s[0]);
}
if (param.getSkuPrice().endsWith("_")){
rangeQuery.gte(s[0]);
}
}
boolQuery.filter(rangeQuery);
}
sourceBuilder.query(boolQuery);
if (!StringUtils.isEmpty(param.getSort())){
String sort = param.getSort();
String[] s = sort.split("_");
SortOrder order = s[1].equalsIgnoreCase("asc")?SortOrder.ASC:SortOrder.DESC;
sourceBuilder.sort(s[0],order);
}
sourceBuilder.from((param.getPageNum()-1)*EsConstant.PRODUCT_PAGESIZE);
sourceBuilder.size(EsConstant.PRODUCT_PAGESIZE);
if (!StringUtils.isEmpty(param.getKeyword())){
HighlightBuilder builder = new HighlightBuilder();
builder.field("skuTitle");
builder.preTags("<b style='color:red'>");
builder.postTags("</b>");
sourceBuilder.highlighter(builder);
}
TermsAggregationBuilder brand_agg = AggregationBuilders.terms("brand_agg");
brand_agg.field("brandId").size(50);
brand_agg.subAggregation(AggregationBuilders.terms("brand_name_agg").field("brandName").size(1));
brand_agg.subAggregation(AggregationBuilders.terms("brand_img_agg").field("brandImg").size(1));
sourceBuilder.aggregation(brand_agg);
TermsAggregationBuilder catalog_agg = AggregationBuilders.terms("catalog_agg").field("catalogId").size(20);
catalog_agg.subAggregation(AggregationBuilders.terms("catalog_name_agg").field("catalogName").size(1));
sourceBuilder.aggregation(catalog_agg);
NestedAggregationBuilder attr_agg = AggregationBuilders.nested("attr_agg", "attrs");
TermsAggregationBuilder attr_id_agg = AggregationBuilders.terms("attr_id_agg").field("attrs.attrId");
attr_id_agg.subAggregation(AggregationBuilders.terms("attr_name_agg").field("attrs.attrName").size(1));
attr_id_agg.subAggregation(AggregationBuilders.terms("attr_value_agg").field("attrs.attrValue"));
attr_agg.subAggregation(attr_id_agg);
sourceBuilder.aggregation(attr_agg);
String s = sourceBuilder.toString();
System.out.println("构建的dsl语句:" + s);
SearchRequest searchRequest = new SearchRequest(new String[]{
EsConstant.PRODUCT_INDEX}, sourceBuilder);
return searchRequest;
}
}
1.1 渲染检索页面
<!Doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="/static/search/css/index.css">
<link rel="stylesheet" type="text/css" href="/static/search/font/iconfont.css">
<script src="/static/search/js/jquery-1.12.4.js"></script>
<title>Document</title>
</head>
<body>
<div class="header_head">
<div class="header_head_box">
<b class="header_head_p">
<div style="overflow: hidden">
<a href="http://gulimall.com" class="header_head_p_a1" style="width:73px;">
谷粒商城首页
</a>
<a href="/static/search/#" class="header_head_p_a">
北京</a>
</div>
<div class="header_head_p_cs">
<a href="/static/search/#" style="background: #C81623;color: #fff;">北京</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>
</b>
<ul>
<li>
<a href="http://auth.gulimall.com/login.html" th:if="${session.loginUser==null}" class="li_2">你好,请登录</a>
<a th:else class="li_2" style="width: 100px">欢迎:</a>
</li>
<li>
<a th:if="${session.loginUser==null}" href="http://auth.gulimall.com/reg.html">免费注册</a>
</li>
<span>|</span>
<li>
<a href="/static/search/#">我的订单</a>
</li>
<span>|</span>
<li class="header_wdjd" style="width:80px;">
<a href="/static/search/#">我的谷粒商城</a>
<img src="/static/search/image/[email protected]"/>
<div class="header_wdjd_txt">
<ul>
<li>
<a href="/static/search/#">待处理订单</a>
</li>
<li>
<a href="/static/search/#">消息</a>
</li>
<li>
<a href="/static/search/#">返修退换货</a>
</li>
<li>
<a href="/static/search/#">我的回答</a>
</li>
<li>
<a href="/static/search/#">降价商品</a>
</li>
<li>
<a href="/static/search/#">我的关注</a>
</li>
</ul>
<ul>
<li>
<a href="/static/search/#">我的京豆</a>
</li>
<li>
<a href="/static/search/#">我的优惠券</a>
</li>
<li>
<a href="/static/search/#">我的白条</a>
</li>
<li>
<a href="/static/search/#">我的理财</a>
</li>
</ul>
</div>
</li>
<span>|</span>
<li>
<a href="/static/search/#">谷粒商城会员</a>
</li>
<span>|</span>
<li>
<a href="/static/search/#">企业采购</a>
</li>
<span>|</span>
<li class="header_wdjd1">
<a href="/static/search/#">客户服务</a>
<img src="/static/search/image/[email protected]"/>
<div class="header_wdjd_txt">
<ul>
<p style="width:100%;">客户</p>
<li>
<a href="/static/search/#">帮助中心</a>
</li>
<li>
<a href="/static/search/#">售后服务</a>
</li>
<li>
<a href="/static/search/#">在线客服</a>
</li>
<li>
<a href="/static/search/#">意见建议</a>
</li>
<li>
<a href="/static/search/#">电话客服</a>
</li>
<li>
<a href="/static/search/#">客服邮箱</a>
</li>
<li>
<a href="/static/search/#">金融资讯</a>
</li>
<li>
<a href="/static/search/#">售全球客服</a>
</li>
</ul>
<ul>
<p style="width:100%;">商户</p>
<li>
<a href="/static/search/#">合作招商</a>
</li>
<li>
<a href="/static/search/#">学习中心</a>
</li>
<li>
<a href="/static/search/#">商家后台</a>
</li>
<li>
<a href="/static/search/#">京麦工作台</a>
</li>
<li>
<a href="/static/search/#">商家帮助</a>
</li>
<li>
<a href="/static/search/#">规则平台</a>
</li>
</ul>
</div>
</li>
<span>|</span>
<li class="header_wzdh">
<a href="/static/search/#">网站导航</a>
<img src="/static/search/image/[email protected]"/>
<div class="header_wzdh_txt">
<ul style="width: 25%;">
<p style="width:100%;">特色主题</p>
<li>
<a href="/static/search/#">谷粒商城试用</a>
</li>
<li>
<a href="/static/search/#">谷粒商城金融</a>
</li>
<li>
<a href="/static/search/#">全球售</a>
</li>
<li>
<a href="/static/search/#">国际站</a>
</li>
<li>
<a href="/static/search/#">谷粒商城会员</a>
</li>
<li>
<a href="/static/search/#">谷粒商城预售</a>
</li>
<li>
<a href="/static/search/#">买什么</a>
</li>
<li>
<a href="/static/search/#">俄语站</a>
</li>
<li>
<a href="/static/search/#">装机大师</a>
</li>
<li>
<a href="/static/search/#">0元评测</a>
</li>
<li>
<a href="/static/search/#">定期送</a>
</li>
<li>
<a href="/static/search/#">港澳售</a>
</li>
<li>
<a href="/static/search/#">优惠券</a>
</li>
<li>
<a href="/static/search/#">秒杀</a>
</li>
<li>
<a href="/static/search/#">闪购</a>
</li>
<li>
<a href="/static/search/#">印尼站</a>
</li>
<li>
<a href="/static/search/#">谷粒商城金融科技</a>
</li>
<li>
<a href="/static/search/#">In货推荐</a>
</li>
<li>
<a href="/static/search/#">陪伴计划</a>
</li>
<li>
<a href="/static/search/#">出海招商</a>
</li>
</ul>
<ul style="width: 20%;">
<p style="width:100%;">行业频道</p>
<li>
<a href="/static/search/#">手机</a>
</li>
<li>
<a href="/static/search/#">智能数码</a>
</li>
<li>
<a href="/static/search/#">玩3c</a>
</li>
<li>
<a href="/static/search/#">电脑办公</a>
</li>
<li>
<a href="/static/search/#">家用电器</a>
</li>
<li>
<a href="/static/search/#">谷粒商城智能</a>
</li>
<li>
<a href="/static/search/#">服装城</a>
</li>
<li>
<a href="/static/search/#">美妆馆</a>
</li>
<li>
<a href="/static/search/#">家装城</a>
</li>
<li>
<a href="/static/search/#">母婴</a>
</li>
<li>
<a href="/static/search/#">食品</a>
</li>
<li>
<a href="/static/search/#">运动户外</a>
</li>
<li>
<a href="/static/search/#">农资频道</a>
</li>
<li>
<a href="/static/search/#">整车</a>
</li>
<li>
<a href="/static/search/#">图书</a>
</li>
</ul>
<ul style="width: 21%;">
<p style="width:100%;">生活服务</p>
<li>
<a href="/static/search/#">白条</a>
</li>
<li>
<a href="/static/search/#">谷粒商城金融App</a>
</li>
<li>
<a href="/static/search/#">谷粒商城小金库</a>
</li>
<li>
<a href="/static/search/#">理财</a>
</li>
<li>
<a href="/static/search/#">智能家电</a>
</li>
<li>
<a href="/static/search/#">话费</a>
</li>
<li>
<a href="/static/search/#">水电煤</a>
</li>
<li>
<a href="/static/search/#">彩票</a>
</li>
<li>
<a href="/static/search/#">旅行</a>
</li>
<li>
<a href="/static/search/#">机票酒店</a>
</li>
<li>
<a href="/static/search/#">电影票</a>
</li>
<li>
<a href="/static/search/#">谷粒商城到家</a>
</li>
<li>
<a href="/static/search/#">谷粒商城众测</a>
</li>
<li>
<a href="/static/search/#">游戏</a>
</li>
</ul>
<ul style="width: 23%; border-right: 0;">
<p style="width:100%;">更多精选</p>
<li>
<a href="/static/search/#">合作招商</a>
</li>
<li>
<a href="/static/search/#">谷粒商城通信</a>
</li>
<li>
<a href="/static/search/#">谷粒商城E卡</a>
</li>
<li>
<a href="/static/search/#">企业采购</a>
</li>
<li>
<a href="/static/search/#">服务市场</a>
</li>
<li>
<a href="/static/search/#">办公生活馆</a>
</li>
<li>
<a href="/static/search/#">乡村招募</a>
</li>
<li>
<a href="/static/search/#">校园加盟</a>
</li>
<li>
<a href="/static/search/#">京友帮</a>
</li>
<li>
<a href="/static/search/#">谷粒商城社区</a>
</li>
<li>
<a href="/static/search/#">智能社区</a>
</li>
<li>
<a href="/static/search/#">游戏社区</a>
</li>
<li>
<a href="/static/search/#">知识产权维权</a>
</li>
</ul>
</div>
</li>
<span>|</span>
<li class="header_sjjd">
<a href="/static/search/#">手机谷粒商城</a>
<div class="header_sjjd_div">
<img src="/static/search/img/01.png"/>
</div>
</li>
</ul>
</div>
</div>
<div class="header_sous">
<div class="logo">
<a href="http://gulimall.com"><img src="/static/search/image/logo1.jpg" alt=""></a>
</div>
<div class="header_form">
<input type="text" placeholder="手机" id="keyword" th:value="${param.keyword}"/>
<a href="javascript:searchByKeyword()" >搜索</a>
</div>
<div class="header_ico">
<div class="header_gw">
<span><a href="/static/search/#">我的购物车</a></span>
<img src="/static/search/image/[email protected]"/>
<span>0</span>
</div>
<div class="header_ko">
<p>购物车中还没有商品,赶紧选购吧!</p>
</div>
</div>
<div class="header_form_nav">
<ul>
<li>
<a href="/static/search/#">谷粒商城之家</a>
</li>
<li>
<a href="/static/search/#">谷粒商城专卖店</a>
</li>
<li>
<a href="/static/search/#">平板</a>
</li>
<li>
<a href="/static/search/#">电脑</a>
</li>
<li>
<a href="/static/search/#">ipad</a>
</li>
</ul>
</div>
<nav>
<ul>
<li class="nav_li1">
<a href="/static/search/#">全部商品分类</a>
</li>
<li class="nav_li">
<a href="/static/search/#">服装城</a>
</li>
<li class="nav_li">
<a href="/static/search/#">没装馆</a>
</li>
<li class="nav_li">
<a href="/static/search/#">超市</a>
</li>
<li class="nav_li">
<a href="/static/search/#">生鲜</a>
</li>
</ul>
<div class="spacer">|</div>
<ul>
<li class="nav_li">
<a href="/static/search/#">全球购</a>
</li>
<li class="nav_li">
<a href="/static/search/#">闪购</a>
</li>
<li class="nav_li">
<a href="/static/search/#">拍卖</a>
</li>
</ul>
<div class="spacer">|</div>
<ul>
<li class="nav_li">
<a href="/static/search/#">金融</a>
</li>
</ul>
</nav>
<div class="header_main_left">
<ul>
<li>
<a href="/static/search/#" class="header_main_left_a"><b>家用电器</b></a>
</li>
<li class="header_li2">
<a href="/static/search/#" class="header_main_left_a"><b>手机</b> / <b>运营商</b> / <b>数码</b></a>
<div class="header_main_left_main">
<div class="header_sj">
<a href="/static/search/#" class="header_sj_a">玩3c</a>
<a href="/static/search/#" class="header_sj_a">手机频道</a>
<a href="/static/search/#" class="header_sj_a">网上营业厅</a>
<a href="/static/search/#" class="header_sj_a">配件选购中心</a>
<a href="/static/search/#" class="header_sj_a">企业购</a>
<a href="/static/search/#" class="header_sj_a">以旧换新</a>
</div>
<ol class="header_ol">
<a href="/static/search/#" style="color: #111;" class="aaa">手机通讯 ></a>
<li>
<a href="/static/search/#" style="color: #999;">手机</a>
<a href="/static/search/#" style="color: #999;">对讲机</a>
<a href="/static/search/#" style="color: #999;">手机维修</a>
<a href="/static/search/#" style="color: #999;">以旧换新</a>
</li>
<a href="/static/search/#" style="color: #111;" class="aaa">运营商 ></a>
<li>
<a href="/static/search/#" style="color: #999;">合约机</a>
<a href="/static/search/#" style="color: #999;">固话宽带</a>
<a href="/static/search/#" style="color: #999;">办套餐</a>
<a href="/static/search/#" style="color: #999;">从话费/流量</a>
<a href="/static/search/#" style="color: #999;">中国电信</a>
<a href="/static/search/#" style="color: #999;">中国移动</a>
<a href="/static/search/#" style="color: #999;">中国联通</a>
<a href="/static/search/#" style="color: #999;">谷粒商城通信</a>
<a href="/static/search/#" style="color: #999;">170选号</a>
</li>
<a href="/static/search/#" style="color: #111;" class="aaa">手机配件 ></a>
<li style="height: 60px;">
<a href="/static/search/#" style="color: #999;">手机壳</a>
<a href="/static/search/#" style="color: #999;">贴膜</a>
<a href="/static/search/#" style="color: #999;">手机储存卡</a>
<a href="/static/search/#" style="color: #999;">数据线</a>
<a href="/static/search/#" style="color: #999;">存电器</a>
<a href="/static/search/#" style="color: #999;">手机耳机</a>
<a href="/static/search/#" style="color: #999;">创业配件</a>
<a href="/static/search/#" style="color: #999;">手机饰品</a>
<a href="/static/search/#" style="color: #999;">手机电池</a>
<a href="/static/search/#" style="color: #999;">苹果周边</a>
<a href="/static/search/#" style="color: #999;">移动电源</a>
<a href="/static/search/#" style="color: #999;">蓝牙耳机</a>
<a href="/static/search/#" style="color: #999;">手机支架</a>
<a href="/static/search/#" style="color: #999;">车载配件</a>
<a href="/static/search/#" style="color: #999;">拍照配件</a>
</li>
<a href="/static/search/#" style="color: #111;" class="aaa">摄影摄像 ></a>
<li style="height: 60px;">
<a href="/static/search/#" style="color: #999;">数码相机</a>
<a href="/static/search/#" style="color: #999;">单电/微单相机</a>
<a href="/static/search/#" style="color: #999;">单反相机</a>
<a href="/static/search/#" style="color: #999;">拍立得</a>
<a href="/static/search/#" style="color: #999;">运动相机</a>
<a href="/static/search/#" style="color: #999;">摄像机</a>
<a href="/static/search/#" style="color: #999;">镜头</a>
<a href="/static/search/#" style="color: #999;">户外器材</a>
<a href="/static/search/#" style="color: #999;">影棚器材</a>
<a href="/static/search/#" style="color: #999;">冲印服务</a>
<a href="/static/search/#" style="color: #999;">数码相框</a>
</li>
<a href="/static/search/#" style="color: #111;" class="aaa">数码配件 ></a>
<li style="height: 60px;">
<a href="/static/search/#" style="color: #999;">三脚架/云台</a>
<a href="/static/search/#" style="color: #999;">相机包</a>
<a href="/static/search/#" style="color: #999;">滤镜</a>
<a href="/static/search/#" style="color: #999;">散光灯/手柄</a>
<a href="/static/search/#" style="color: #999;">相机清洁</a>
<a href="/static/search/#" style="color: #999;">机身附件</a>
<a href="/static/search/#" style="color: #999;">镜头附件</a>
<a href="/static/search/#" style="color: #999;">读卡器</a>
<a href="/static/search/#" style="color: #999;">支架</a>
<a href="/static/search/#" style="color: #999;">电池/存电器</a>
</li>
<a href="/static/search/#" style="color: #111;" class="aaa">影音娱乐 ></a>
<li>
<a href="/static/search/#" style="color: #999;">耳机/耳麦</a>
<a href="/static/search/#" style="color: #999;">音箱/音响</a>
<a href="/static/search/#" style="color: #999;">智能音箱</a>
<a href="/static/search/#" style="color: #999;">便携/无线音箱</a>
<a href="/static/search/#" style="color: #999;">收音机</a>
<a href="/static/search/#" style="color: #999;">麦克风</a>
<a href="/static/search/#" style="color: #999;">MP3/MP4</a>
<a href="/static/search/#" style="color: #999;">专业音频</a>
</li>
<a href="/static/search/#" style="color: #111;" class="aaa">智能设备 ></a>
<li style="height: 60px;">
<a href="/static/search/#" style="color: #999;">智能手环</a>
<a href="/static/search/#" style="color: #999;">智能手表</a>
<a href="/static/search/#" style="color: #999;">智能眼镜</a>
<a href="/static/search/#" style="color: #999;">智能机器人</a>
<a href="/static/search/#" style="color: #999;">运动跟踪器</a>
<a href="/static/search/#" style="color: #999;">健康监测</a>
<a href="/static/search/#" style="color: #999;">智能配饰</a>
<a href="/static/search/#" style="color: #999;">智能家居</a>
<a href="/static/search/#" style="color: #999;">体感车</a>
<a href="/static/search/#" style="color: #999;">无人机</a>
<a href="/static/search/#" style="color: #999;">其他配件</a>
</li>
<a href="/static/search/#" style="color: #111;" class="aaa">电子教育 ></a>
<li>
<a href="/static/search/#" style="color: #999;">学生平板</a>
<a href="/static/search/#" style="color: #999;">点读机</a>
<a href="/static/search/#" style="color: #999;">早教益智</a>
<a href="/static/search/#" style="color: #999;">录音笔</a>
<a href="/static/search/#" style="color: #999;">电纸书</a>
<a href="/static/search/#" style="color: #999;">电子词典</a>
<a href="/static/search/#" style="color: #999;">复读机</a>
</li>
</ol>
<div class="header_r">
<div class="header_r_tu">
<a href="/static/search/#"><img src="/static/search/img/56b2f385n8e4eb051.jpg"/></a>
<a href="/static/search/#"><img src="/static/search/img/56b2f385n8e4eb051.jpg"/></a>
<a href="/static/search/#"><img src="/static/search/img/56b2f385n8e4eb051.jpg"/></a>
<a href="/static/search/#"><img src="/static/search/img/56b2f385n8e4eb051.jpg"/></a>
<a href="/static/search/#"><img src="/static/search/img/56b2f385n8e4eb051.jpg"/></a>
<a href="/static/search/#"><img src="/static/search/img/56b2f385n8e4eb051.jpg"/></a>
<a href="/static/search/#"><img src="/static/search/img/56b2f385n8e4eb051.jpg"/></a>
<a href="/static/search/#"><img src="/static/search/img/56b2f385n8e4eb051.jpg"/></a>
</div>
<div class="header_r_tu1">
<a href="/static/search/#"><img src="/static/search/img/JD_ash7 - 副本.png"/></a>
<a href="/static/search/#"><img src="/static/search/img/JD_ash6.png"/></a>
</div>
</div>
</div>
</li>
<li>
<a href="/static/search/#" class="header_main_left_a"><b>电脑</b> / <b>办公</b></a>
</li>
<li>
<a href="/static/search/#" class="header_main_left_a"><b>家居</b> / <b>家具</b> / <b>家装</b> / <b>厨具</b></a>
</li>
<li>
<a href="/static/search/#" class="header_main_left_a"><b>男装</b> / <b>女装</b> / <b>童装</b> / <b>内衣</b></a>
</li>
<li>
<a href="/static/search/#" class="header_main_left_a"><b>美妆个护 </b>/ <b>宠物</b></a>
</li>
<li>
<a href="/static/search/#" class="header_main_left_a"><b>女鞋</b> / <b>箱包</b> / <b>钟表</b> / <b>珠宝</b></a>
</li>
<li>
<a href="/static/search/#" class="header_main_left_a"><b>男鞋</b> / <b>运动</b> / <b>户外</b></a>
</li>
<li>
<a href="/static/search/#" class="header_main_left_a"><b>汽车</b> / <b>汽车用品</b></a>
</li>
<li>
<a href="/static/search/#" class="header_main_left_a"><b>母婴</b> / <b>玩具乐器</b></a>
</li>
<li>
<a href="/static/search/#" class="header_main_left_a"><b>食品</b> / <b>酒类</b> / <b>生鲜</b> / <b>特产</b></a>
</li>
<li>
<a href="/static/search/#" class="header_main_left_a"><b>礼品鲜花</b> / <b>农资绿植</b></a>
</li>
<li>
<a href="/static/search/#" class="header_main_left_a"><b>医药保健</b> / <b>计生情趣</b></a>
</li>
<li>
<a href="/static/search/#" class="header_main_left_a"><b>图书</b> / <b>音箱</b>/ <b>电子书</b></a>
</li>
<li>
<a href="/static/search/#" class="header_main_left_a"><b>机票</b> / <b>酒店</b> / <b>旅游</b> / <b>生活</b></a>
</li>
<li>
<a href="/static/search/#" class="header_main_left_a"><b>理财</b> / <b>众筹</b> / <b>白条</b> / <b>保险</b></a>
</li>
</ul>
</div>
</div>
<hr style="border: 1px solid red;margin-top: -7px;">
<div class="JD_temai">
<div class="JD_main">
<div class="JD_left">
<div class="hd">
热卖推荐
</div>
<div class="bd mc">
<ul class="mc">
<li>
<a href="/static/search/#" class="mc_a"><img src="/static/search/img/5a28b5a1n8a5c095f.jpg"
alt=""></a>
<div class="mc_div">
<a href="/static/search/#" class="mc_div_a1">
<em>华为 HUAWEI nova 2S 全面屏四摄 6GB +64GB 曜石黑 移动联通电信4G手机 双卡双待</em>
</a>
<p>
<strong>
<em class="number J-p-5963064">¥2999.00</em>
</strong>
</p>
<a href="/static/search/#" class="mc_div_a2">立即抢购</a>
</div>
</li>
<li>
<a href="/static/search/#" class="mc_a"><img src="/static/search/img/59f5eef1n99542494.jpg"
alt=""></a>
<div class="mc_div">
<a href="/static/search/#" class="mc_div_a1">
<em>【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待</em>
</a>
<p>
<strong>
<em class="number J-p-5963064">¥1699.00</em>
</strong>
</p>
<a href="/static/search/#" class="mc_div_a2">立即抢购</a>
</div>
</li>
<li style="margin-right: 0">
<a href="/static/search/#" class="mc_a"><img src="/static/search/img/59f5eef1n99542494.jpg"
alt=""></a>
<div class="mc_div">
<a href="/static/search/#" class="mc_div_a1">
<em>华为 HUAWEI nova 2S 全面屏四摄 6GB +64GB 曜石黑 移动联通电信4G手机 双卡双待</em>
</a>
<p>
<strong>
<em class="number J-p-5963064">¥2999.00</em>
</strong>
</p>
<a href="/static/search/#" class="mc_div_a2">立即抢购</a>
</div>
</li>
</ul>
</div>
</div>
<div class="JD_right">
<div class="hd"> 促销活动</div>
<div class="bd">
<ul>
<li> . <a href="/static/search/#">红米千元全面屏手机上市</a></li>
<li> . <a href="/static/search/#">锤子坚果Pro2火爆预约中</a></li>
<li> . <a href="/static/search/#">大牌新品 疯狂抢购</a></li>
<li> . <a href="/static/search/#">X20 vivo蓝新色上市</a></li>
<li> . <a href="/static/search/#">荣耀畅玩7X新品上市</a></li>
</ul>
</div>
</div>
</div>
</div>
<div class="JD_ipone">
<div class="JD_ipone_bar">
<div class="JD_ipone_one a">
<a href="/static/search/#">手机</a>
</div>
<i><img src="/static/search/image/[email protected]" alt=""></i>
<div class="JD_ipone_one b">
<a href="/static/search/#" class="qqq">手机通讯录 <img src="/static/search/image/[email protected]" alt=""></a>
<div>
<a href="/static/search/#">手机通讯</a>
<a href="/static/search/#">运营商</a>
<a href="/static/search/#">手机配件</a>
<a href="/static/search/#">手机服务</a>
</div>
</div>
<i><img src="/static/search/image/[email protected]" alt=""></i>
<div class="JD_ipone_one c">
<a href="/static/search/#" class="qqq">手机 <img src="/static/search/image/[email protected]" alt=""></a>
<div>
<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>
<div class="JD_ipone_one c">
<a th:href="${nav.linkUrl}" th:each="nav : ${result.navs}"><span th:text="${nav.navName}"></span> : <span th:text="${nav.navValue}"></span> x</a>
</div>
<i><img src="/static/search/image/[email protected]" alt=""></i>
</div>
</div>
<div class="JD_banner w">
<div class="JD_nav">
<div class="JD_selector">
<div class="title">
<h3><b>手机</b><em>商品筛选</em></h3>
<div class="st-ext">共 <span>10135</span>个商品</div>
</div>
<div class="JD_nav_logo" th:with="brandId = ${param.brandId}">
<div class="JD_nav_wrap" th:if="${#strings.isEmpty(brandId)}">
<div class="sl_key">
<span><b>品牌:</b></span>
</div>
<div class="sl_value">
<div class="sl_value_logo">
<ul>
<li th:each="brand : ${result.brands}">
<a href="/static/search/#" th:href="${
'javascript:searchProducts("brandId",'+brand.brandId+')'}">
<img th:src="${brand.brandImg}" alt="">
<div th:text="${brand.brandName}">
华为(HUAWEI)
</div>
</a>
</li>
</ul>
</div>
</div>
<div class="sl_ext">
<a href="/static/search/#">
更多
<i style='background: url("image/search.ele.png")no-repeat 3px 7px'></i>
<b style='background: url("image/search.ele.png")no-repeat 3px -44px'></b>
</a>
<a href="/static/search/#">
多选
<i>+</i>
<span>+</span>
</a>
</div>
</div>
<div class="JD_pre">
<div class="sl_key">
<span><b>分类:</b></span>
</div>
<div class="sl_value">
<ul>
<li th:each="catalog : ${result.catalogs}">
<a href="/static/search/#" th:text="${catalog.catalogName}" th:href="${
'javascript:searchProducts("catalog3Id",'+catalog.catalogId+')'}">5.56英寸及以上</a>
</li>
</ul>
</div>
<div class="sl_ext">
<a href="/static/search/#">
更多f
<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>
<ul>
<li th:each="value : ${attr.attrValue}">
<a href="/static/search/#" th:text="${value}" th:href="${
'javascript:searchProducts("attrs","'+attr.attrId+'_'+value+'")'}">5.56英寸及以上</a>
</li>
</ul>
</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" th:with="p = ${param.sort},priceRange = ${param.skuPrice}">
<a sort="hotScore"
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;cursor:pointer;':'color:#333;border-color:#CCC;background:#FFF;cursor:pointer;'}"
>综合排序 [[${(!#strings.isEmpty(p) && #strings.startsWith(p,'hotScore') && #strings.endsWith(p,'desc'))? '↓':((!#strings.isEmpty(p) && #strings.endsWith(p,'asc') && #strings.startsWith(p,'hotScore'))?'↑':'')}]]</a>
<a sort="saleCount" 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;cursor:pointer;':'color:#333;border-color:#CCC;background:#FFF;cursor:pointer;'}"
>销量 [[${(!#strings.isEmpty(p) && #strings.startsWith(p,'saleCount') && #strings.endsWith(p,'desc'))? '↓':((!#strings.isEmpty(p) && #strings.startsWith(p,'saleCount') && #strings.endsWith(p,'asc'))?'↑':'')}]]</a>
<a sort="skuPrice" 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;cursor:pointer;':'color:#333;border-color:#CCC;background:#FFF;cursor:pointer;'}"
>价格 [[${(!#strings.isEmpty(p) && #strings.startsWith(p,'skuPrice') && #strings.endsWith(p,'desc'))? '↓':((!#strings.isEmpty(p)&& #strings.startsWith(p,'skuPrice') && #strings.endsWith(p,'asc'))?'↑':'')}]]</a>
<a sort="hotScore" class="sort_a">评论分</a>
<a sort="hotScore" class="sort_a">上架时间</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>
</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/[email protected]" alt=""></li>
<li id="tab2" value="2">朝阳 <img src="/static/search/image/[email protected]" alt=""></li>
<li id="tab3" value="3">三环以内 <img src="/static/search/image/[email protected]" alt="">
</li>
</ul>
<div id="container">
<div id="content1" style="z-index: 1;">
<a href="/static/search/#">北京</a>
<a href="/static/search/#">上海</a>
<a href="/static/search/#">天津</a>
<a href="/static/search/#">重庆</a>
<a href="/static/search/#">河北</a>
<a href="/static/search/#">山西</a>
<a href="/static/search/#">河南</a>
<a href="/static/search/#">辽宁</a>
<a href="/static/search/#">吉林</a>
<a href="/static/search/#">黑龙江</a>
<a href="/static/search/#">内蒙古</a>
<a href="/static/search/#">江苏</a>
<a href="/static/search/#">山东</a>
<a href="/static/search/#">安徽</a>
<a href="/static/search/#">浙江</a>
<a href="/static/search/#">福建</a>
<a href="/static/search/#">湖北</a>
<a href="/static/search/#">湖南</a>
<a href="/static/search/#">广东</a>
<a href="/static/search/#">广西</a>
<a href="/static/search/#">江西</a>
<a href="/static/search/#">四川</a>
<a href="/static/search/#">海南</a>
<a href="/static/search/#">贵州</a>
<a href="/static/search/#">云南</a>
<a href="/static/search/#">西藏</a>
<a href="/static/search/#">陕西</a>
<a href="/static/search/#">甘肃</a>
<a href="/static/search/#">青海</a>
<a href="/static/search/#">宁夏</a>
<a href="/static/search/#">新疆</a>
<a href="/static/search/#">港澳</a>
<a href="/static/search/#">台湾</a>
<a href="/static/search/#">钓鱼岛</a>
<a href="/static/search/#">海外</a>
</div>
<div id="content2">
<a href="/static/search/#">朝阳区</a>
<a href="/static/search/#">海淀区</a>
<a href="/static/search/#">西城区</a>
<a href="/static/search/#">东城区</a>
<a href="/static/search/#">大兴区</a>
<a href="/static/search/#">丰台区</a>
<a href="/static/search/#">昌平区</a>
<a href="/static/search/#">顺义区</a>
</div>
<div id="content3">
<a href="/static/search/#">三环以内</a>
<a href="/static/search/#">管庄</a>
<a href="/static/search/#">北苑</a>
<a href="/static/search/#">定福庄</a>
<a href="/static/search/#">三环到四环之间</a>
<a href="/static/search/#">四环到五环之间</a>
<a href="/static/search/#">五环到六环之间</a>
</div>
</div>
</div>
</div>
<div class="filter_bottom_right">
<ul>
<li>
<a href="/static/search/#">
<i></i>
谷粒商城配送
</a>
</li>
<li>
<a href="/static/search/#">
<i></i>
京尊达 </a>
</li>
<li>
<a href="/static/search/#">
<i></i>
货到付款
</a>
</li>
<li>
<a href="#" th:with="check = ${param.hasStock}">
<input id="showHasStock" type="checkbox" th:checked="${#strings.equals(check,'1')?true:false}">
仅显示有货
</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 th:href="|http://item.gulimall.com/${product.skuId}.html|"
title="购买AppleCare+,获得原厂2年整机保修(含电池),和多达2次意外损坏的保修服务。购买勾选:保障服务、原厂保2年。">
<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 th:href="|http://item.gulimall.com/${product.skuId}.html|" 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 class="page_a" th:if="${result.pageNum>1}" th:attr="pn=${result.pageNum - 1}" style="cursor:pointer;">
< 上一页
</a>
<a class="page_a" th:attr="pn=${nav},style=${nav == result.pageNum ? 'border: 0;color:#ee2222;background: #fff;':'cursor:pointer;'}" th:each="nav:${result.pageNavs}">[[${nav}]]</a>
<a class="page_a" th:if="${result.pageNum<result.totalPages}" th:attr="pn=${result.pageNum + 1}" style="cursor:pointer;">
下一页 >
</a>
</span>
<span class="page_span2">
<em>共<b>[[${result.totalPages}]]</b>页 到第</em>
<input type="number" value="1" oninput="if(value<=0)value=1;" id="jumpPage">
<em>页</em>
<a th:href="${
'javascript:jumpPage()'}">确定</a>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="JD_jx">
<div class="JD_jx_title">
<div class="mt">
<strong class="mt-title">商品精选</strong>
<img src="/static/search/image/u-ad.gif" alt="">
</div>
<div class="mc">
<ul>
<li>
<div class="mc_img">
<a href="/static/search/#" title="【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待">
<img src="/static/search/img/5a25ffc7N98b35d49.jpg" alt="">
</a>
</div>
<div class="mc_name">
<a href="/static/search/#" title="【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待">
<em>【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待</em>
</a>
</div>
<div class="mc_price">
<strong>
<span>¥1999.00</span>
</strong>
<span class="mc_ico" title="购买本商品送赠品">赠品</span>
</div>
<div class="mc_rev">
<a href="/static/search/#">15930</a>
<span>人好评</span>
</div>
</li>
<li>
<div class="mc_img">
<a href="/static/search/#" title="【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待">
<img src="/static/search/img/5a25ffc7N98b35d49.jpg" alt="">
</a>
</div>
<div class="mc_name">
<a href="/static/search/#" title="【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待">
<em>【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待</em>
</a>
</div>
<div class="mc_price">
<strong>
<span>¥1999.00</span>
</strong>
<span class="mc_ico" title="购买本商品送赠品">赠品</span>
</div>
<div class="mc_rev">
<a href="/static/search/#">15930</a>
<span>人好评</span>
</div>
</li>
<li>
<div class="mc_img">
<a href="/static/search/#" title="【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待">
<img src="/static/search/img/5a25ffc7N98b35d49.jpg" alt="">
</a>
</div>
<div class="mc_name">
<a href="/static/search/#" title="【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待">
<em>【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待</em>
</a>
</div>
<div class="mc_price">
<strong>
<span>¥1999.00</span>
</strong>
<span class="mc_ico" title="购买本商品送赠品">赠品</span>
</div>
<div class="mc_rev">
<a href="/static/search/#">15930</a>
<span>人好评</span>
</div>
</li>
<li>
<div class="mc_img">
<a href="/static/search/#" title="【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待">
<img src="/static/search/img/5a25ffc7N98b35d49.jpg" alt="">
</a>
</div>
<div class="mc_name">
<a href="/static/search/#" title="【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待">
<em>【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待</em>
</a>
</div>
<div class="mc_price">
<strong>
<span>¥1999.00</span>
</strong>
<span class="mc_ico" title="购买本商品送赠品">赠品</span>
</div>
<div class="mc_rev">
<a href="/static/search/#">15930</a>
<span>人好评</span>
</div>
</li>
<li>
<div class="mc_img">
<a href="/static/search/#" title="【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待">
<img src="/static/search/img/5a25ffc7N98b35d49.jpg" alt="">
</a>
</div>
<div class="mc_name">
<a href="/static/search/#" title="【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待">
<em>【预约版】华为 HUAWEI 畅享7S 全面屏双摄 4GB +64GB 黑色 移动联通电信4G手机 双卡双待</em>
</a>
</div>
<div class="mc_price">
<strong>
<span>¥1999.00</span>
</strong>
<span class="mc_ico" title="购买本商品送赠品">赠品</span>
</div>
<div class="mc_rev">
<a href="/static/search/#">15930</a>
<span>人好评</span>
</div>
</li>
</ul>
</div>
</div>
</div>
<div class="JD_cnxh">
<div class="JD_jx_title">
<div class="mt">
<strong class="mt-title">猜你喜欢</strong>
<a href="/static/search/#">
<img src="/static/search/image/update.png" alt="">
换一批
</a>
</div>
<div class="mc">
<ul>
<li>
<div class="mc_img">
<a href="/static/search/#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机">
<img src="/static/search/img/59bf3c47n91d65c73.jpg" alt="">
</a>
</div>
<div class="mc_name">
<a href="/static/search/#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机">
<em>诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机</em>
</a>
</div>
<div class="mc_rev">
<a href="/static/search/#">已有80万+人评价</a>
</div>
<div class="mc_price">
<strong>
<span>¥1999.00</span>
</strong>
</div>
</li>
<li>
<div class="mc_img">
<a href="/static/search/#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机">
<img src="/static/search/img/5a28b5c6Ndec5088f.jpg" alt=""></a>
</div>
<div class="mc_name">
<a href="/static/search/#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机">
<em>诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机</em>
</a>
</div>
<div class="mc_rev">
<a href="/static/search/#">已有80万+人评价</a>
</div>
<div class="mc_price">
<strong>
<span>¥1999.00</span>
</strong>
</div>
</li>
<li>
<div class="mc_img">
<a href="/static/search/#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机"><img
src="/static/search/img/593e4de0n5ff878a4.jpg" alt=""></a>
</div>
<div class="mc_name">
<a href="/static/search/#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机">
<em>诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机</em>
</a>
</div>
<div class="mc_rev">
<a href="/static/search/#">已有80万+人评价</a>
</div>
<div class="mc_price">
<strong>
<span>¥1999.00</span>
</strong>
</div>
</li>
<li>
<div class="mc_img">
<a href="/static/search/#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机"><img
src="/static/search/img/593e4de0n5ff878a4.jpg" alt=""></a>
</div>
<div class="mc_name">
<a href="/static/search/#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机">
<em>诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机</em>
</a>
</div>
<div class="mc_rev">
<a href="/static/search/#">已有80万+人评价</a>
</div>
<div class="mc_price">
<strong>
<span>¥1999.00</span>
</strong>
</div>
</li>
<li>
<div class="mc_img">
<a href="/static/search/#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机"><img
src="/static/search/img/59c493a7N3f9b9c85 (1).jpg" alt=""></a>
</div>
<div class="mc_name">
<a href="/static/search/#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机">
<em>诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机</em>
</a>
</div>
<div class="mc_rev">
<a href="/static/search/#">已有80万+人评价</a>
</div>
<div class="mc_price">
<strong>
<span>¥1999.00</span>
</strong>
</div>
</li>
<li>
<div class="mc_img">
<a href="/static/search/#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机"><img
src="/static/search/img/59c493a7N3f9b9c85 (1).jpg" alt=""></a>
</div>
<div class="mc_name">
<a href="/static/search/#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机">
<em>诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机</em>
</a>
</div>
<div class="mc_rev">
<a href="/static/search/#">已有80万+人评价</a>
</div>
<div class="mc_price">
<strong>
<span>¥1999.00</span>
</strong>
</div>
</li>
</ul>
</div>
</div>
</div>
<div class="JD_zuji">
<div class="JD_jx_title">
<div class="mt">
<strong class="mt-title">我的足迹</strong>
<a href="/static/search/#">
更多浏览记录
</a>
</div>
<div class="mc">
<ul>
<li>
<div class="mc_img">
<a href="/static/search/#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机">
<img src="/static/search/img/59e58a11Nc38676d5.jpg" alt="">
</a>
</div>
<div class="mc_price">
<strong>
<span>¥2998.00</span>
</strong>
</div>
</li>
<li>
<div class="mc_img">
<a href="/static/search/#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机">
<img src="/static/search/img/5a28acccN73689386.jpg" alt="">
</a>
</div>
<div class="mc_price">
<strong>
<span>¥88.00</span>
</strong>
</div>
</li>
<li>
<div class="mc_img">
<a href="/static/search/#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机">
<img src="/static/search/img/5a1690ddN441b5dce.jpg" alt="">
</a>
</div>
<div class="mc_price">
<strong>
<span>¥199.00</span>
</strong>
</div>
</li>
<li>
<div class="mc_img">
<a href="/static/search/#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机">
<img src="/static/search/img/5a02bde7N7d4453b1.jpg" alt="">
</a>
</div>
<div class="mc_price">
<strong>
<span>¥799.00</span>
</strong>
</div>
</li>
<li>
<div class="mc_img">
<a href="/static/search/#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机">
<img src="/static/search/img/5a122dbeN044ebf19.jpg" alt="">
</a>
</div>
<div class="mc_price">
<strong>
<span>¥599.00</span>
</strong>
</div>
</li>
<li>
<div class="mc_img">
<a href="/static/search/#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机">
<img src="/static/search/img/59c493a7N3f9b9c85.jpg" alt="">
</a>
</div>
<div class="mc_price">
<strong>
<span>¥699.00</span>
</strong>
</div>
</li>
<li>
<div class="mc_img">
<a href="/static/search/#" title="诺基亚 7 (Nokia 7) 4GB+64GB 黑色 全网通 双卡双待 移动联通电信4G手机">
<img src="/static/search/img/5a08f6f6N5bab2c1c.jpg" alt="">
</a>
</div>
<div class="mc_price">
<strong>
<span>¥715.00</span>
</strong>
</div>
</li>
</ul>
</div>
</div>
</div>
<div style="width: 1210px;margin: 0 auto;margin-bottom: 10px"><img src="/static/search/img/5a33a2e0N9a04b4af.jpg"
alt=""></div>
<footer class="footer">
<div class="footer_top">
<ul>
<li>
<span></span>
<h3>品类齐全,轻松购物</h3>
</li>
<li>
<span></span>
<h3>多仓直发,极速配发</h3>
</li>
<li>
<span></span>
<h3>正品行货,精致服务</h3>
</li>
<li>
<span></span>
<h3>天天低价,畅选无忧</h3>
</li>
</ul>
</div>
<div class="footer_center">
<ol>
<li>购物指南</li>
<li><a href="/static/search/#" style="color: rgb(114, 114, 114);">购物流程</a>
</li>
<li><a href="/static/search/#" style="color: rgb(114, 114, 114);">会员介绍</a>
</li>
<li><a href="/static/search/#">生活旅行</a>
</li>
<li><a href="/static/search/#" style="color: rgb(114, 114, 114);">常见问题</a>
</li>
<li><a href="/static/search/#">大家电</a>
</li>
<li><a href="/static/search/#" style="color: rgb(114, 114, 114);">联系客服</a>
</li>
</ol>
<ol>
<li>配送方式</li>
<li><a href="/static/search/#" style="color: rgb(114, 114, 114);">上门自提</a>
</li>
<li><a href="/static/search/#" style="color: rgb(114, 114, 114);">211限时达</a>
</li>
<li><a href="/static/search/#" style="color: rgb(114, 114, 114);">配送服务查询</a>
</li>
<li><a href="/static/search/#" style="color: rgb(114, 114, 114);">配送费收取标准</a>
</li>
<li><a href="/static/search/#" style="color: rgb(114, 114, 114);">海外配送</a>
</li>
</ol>
<ol>
<li>支付方式</li>
<li><a href="/static/search/#" style="color: rgb(114, 114, 114);">货到付款</a>
</li>
<li><a href="/static/search/#" style="color: rgb(114, 114, 114);">在线支付</a>
</li>
<li><a href="/static/search/#" style="color: rgb(114, 114, 114);">分期付款</a>
</li>
<li><a href="/static/search/#" style="color: rgb(114, 114, 114);">邮局汇款</a>
</li>
<li><a href="/static/search/#" style="color: rgb(114, 114, 114);">公司转账</a>
</li>
</ol>
<ol>
<li>售后服务</li>
<li><a href="/static/search/#" style="color: rgb(114, 114, 114);">售后政策</a>
</li>
<li><a href="/static/search/#" style="color: rgb(114, 114, 114);">价格保护</a>
</li>
<li><a href="/static/search/#" style="color: rgb(114, 114, 114);">退款说明</a>
</li>
<li><a href="/static/search/#" style="color: rgb(114, 114, 114);">返修/退换货</a>
</li>
<li><a href="/static/search/#">取消订单</a>
</li>
</ol>
<ol>
<li>特色服务</li>
<li><a href="/static/search/#" style="color: rgb(114, 114, 114);">夺宝岛</a>
</li>
<li><a href="/static/search/#">DIY装机</a>
</li>
<li><a href="/static/search/#" style="color: rgb(114, 114, 114);">延保服务</a>
</li>
<li><a href="/static/search/#" style="color: rgb(114, 114, 114);">谷粒商城E卡</a>
</li>
<li><a href="/static/search/#" style="color: rgb(114, 114, 114);">谷粒商城通信</a>
</li>
<li><a href="/static/search/#" style="color: rgb(114, 114, 114);">谷粒商城gulimall+</a>
</li>
</ol>
<ol>
<li>谷粒商城自营覆盖区域</li>
<li>
谷粒商城已向全国2661个区县提供自<br> 营配送服务,支持货到付款、
<br> POS机刷卡和售后上门服务。
</li>
<li><a href="/static/search/#" style="color: rgb(114, 114, 114);">查看详情></a>
</li>
</ol>
</div>
<div class="footer_foot">
<p class="footer_p p1">
<a href="/static/search/#">关于我们</a>
<span></span>
<a href="/static/search/#" style="color: rgb(114, 114, 114);">联系我们</a>
<span></span>
<a href="/static/search/#">联系客服</a>
<span></span>
<a href="/static/search/#" style="color: rgb(114, 114, 114);">合作招商</a>
<span></span>
<a href="/static/search/#" style="color: rgb(114, 114, 114);">商家帮助</a>
<span></span>
<a href="/static/search/#" style="color: rgb(114, 114, 114);">营销中心</a>
<span></span>
<a href="/static/search/#" style="color: rgb(114, 114, 114);">手机谷粒商城</a>
<span></span>
<a href="/static/search/#" style="color: rgb(114, 114, 114);">友情链接</a>
<span></span>
<a href="/static/search/#" style="color: rgb(114, 114, 114);">销售联盟</a>
<span></span>
<a href="/static/search/#" style="color: rgb(114, 114, 114);">谷粒商城社区</a>
<span></span>
<a href="/static/search/#" style="color: rgb(114, 114, 114);">风险监测</a>
<span></span>
<a href="/static/search/#">隐私政策</a>
<span></span>
<a href="/static/search/#">谷粒商城公益</a>
<span></span>
<a href="/static/search/#" style="color: rgb(114, 114, 114);">English Site</a>
<span></span>
<a href="/static/search/#">media & IR</a>
</p>
<p class="footer_p">
<a href="/static/search/#">京公网安备 11000002000088号</a>
<span></span>
<a href="/static/search/#">京ICP证070359号</a>
<span></span>
<a href="/static/search/#">互联网药品信息服务资格证编号(京)-经营性-2014-0008</a>
<span></span>
<a href="/static/search/#">新出发京零 字第大120007号</a>
</p>
<p class="footer_p">
<a href="/static/search/#">互联网出版许可证编号新出网证(京)字150号</a>
<span></span>
<a href="/static/search/#">出版物经营许可证</a>
<span></span>
<a href="/static/search/#">网络文化经营许可证京网文[2014]2148-348号</a>
<span></span>
<a href="/static/search/#">违法和不良信息举报电话:4006561155</a>
</p>
<p class="footer_p">
<a href="/static/search/#">Copyright © 2004 - 2017 谷粒商城JD.com 版权所有</a>
<span></span>
<a href="/static/search/#">消费者维权热线:4006067733</a>
<a href="/static/search/#">经营证照</a>
</p>
<p class="footer_p">
<a href="/static/search/#">谷粒商城旗下网站:</a>
<a href="/static/search/#">谷粒商城支付</a>
<span></span>
<a href="/static/search/#">谷粒商城云</a>
</p>
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
</div>
</footer>
<div class="header_bar">
<div class="header_bar_box">
<ul>
<li>
<a href="/static/search/#"><img src="/static/search/img/wo.png"/></a>
<div class="div">
<a href="/static/search/#">谷粒商城会员</a>
</div>
</li>
<li>
<a href="/static/search/#"><img src="/static/search/img/gouwuche.png"/></a>
<div class="div">
<a href="/static/search/#">购物车</a>
</div>
</li>
<li>
<a href="/static/search/#"><img src="/static/search/img/taoxin.png"/></a>
<div class="div">
<a href="/static/search/#">我的关注</a>
</div>
</li>
<li>
<a href="/static/search/#"><img src="/static/search/img/shi.png"/></a>
<div class="div">
<a href="/static/search/#">我的足迹</a>
</div>
</li>
<li>
<a href="/static/search/#"><img src="/static/search/img/xinxi.png"/></a>
<div class="div">
<a href="/static/search/#">我的消息</a>
</div>
</li>
<li>
<a href="/static/search/#"><img src="/static/search/img/qianbao.png"/></a>
<div class="div">
<a href="/static/search/#">资讯JIMI</a>
</div>
</li>
</ul>
<ul>
<li>
<a href="/static/search/#"><img src="/static/search/img/fa3f24a70d38bd439261cb7439e517a5.png"/></a>
<div class="div">
<a href="/static/search/#">顶部</a>
</div>
</li>
<li>
<a href="/static/search/#"><img src="/static/search/img/xinxi.png"/></a>
<div class="div">
<a href="/static/search/#">反馈</a>
</div>
</li>
</ul>
</div>
</div>
<script>
$(".sl_ext a:nth-child(1)").hover(function () {
$(this).children("b").stop(true).animate({
top: "3px"}, 50);
$(this).children("i").stop(true).animate({
top: "-23px"}, 50)
}, function () {
$(this).children("b").stop(true).animate({
top: "24px"}, 50);
$(this).children("i").stop(true).animate({
top: "3px"}, 50)
});
$(".sl_ext a:nth-child(2)").hover(function () {
$(this).children("span").stop(true).animate({
top: "-1px"}, 100);
$(this).children("i").stop(true).animate({
top: "-14px"}, 100).css({
display: "none"})
}, function () {
$(this).children("span").stop(true).animate({
top: "14px"}, 100);
$(this).children("i").stop(true).animate({
top: "-1px"}, 100).css({
display: "block"})
});
$('.tab_im img').hover(function () {
var a = $(this).prop('src');
var index = $(this).parents('li').index();
$(this).parents('li').css('border', '2px solid red').siblings('li').css('border', '1px solid #ccc');
$(this).parents('ul').prev().find('img').prop('src', a);
$(this).parents('ul').siblings('.tab_JE').find('a').eq(index).css('display', 'block').siblings('a').css('display', 'none');
$(this).parents('ul').siblings('.tab_R').find('span').eq(index).css('display', 'block').siblings('span').css('display', 'none')
});
$(".JD_ipone_one").hover(function () {
$(this).children("div").css({
display: "block"})
}, function () {
$(this).children("div").css({
display: "none"})
});
$("#tab>li").click(function () {
var i = $(this).index();
$("#container>div").hide().eq(i).show()
});
$(".dizhi_show").hover(function () {
$(".dizhi_con").css({
display: "block"})
}, function () {
$(".dizhi_con").css({
display: "none"})
});
$(".dizhi_con").hover(function () {
$(this).css({
display: "block"})
}, function () {
$(this).css({
display: "none"})
});
var $li = $(".JD_nav_logo>div:gt(3)").hide();
$('.JD_show span').click(function () {
if ($li.is(':hidden')) {
$li.show();
$(this).css({
width: "86px"}).text('收起 ^');
} else {
$li.hide();
$('.JD_show span').css({
width: "291px"}).text('更多选项( CPU核数、网络、机身颜色 等)');
}
return false;
});
$(".rig_tab>div").hover(function () {
var i = $(this).index();
$(this).find('.ico').css({
display: 'block'}).stop(true).animate({
top: "190px"}, 300)
}, function () {
var i = $(this).index();
$(this).find('.ico').css({
display: 'none'}).stop(true).animate({
top: "230px"})
});
$('.header_main_left>ul>li').hover(function () {
$(this).css({
background: "#f0f0f0"
}).find('.header_main_left_main').stop(true).fadeIn(300)
}, function () {
$(this).css({
background: "#fff"
}).find(".header_main_left_a").css({
color: "#000"
});
$(this).find('.header_main_left_main').stop(true).fadeOut(100)
});
$(".header_sj a").hover(function () {
$(this).css({
background: "#444"
})
}, function () {
$(this).css({
background: "#6e6568"
})
});
$(".nav_li1 a").hover(function () {
$(".header_main_left").stop(true).fadeIn()
}, function () {
$(".header_main_left").stop(true).fadeOut()
});
$(".header_main_left").hover(function () {
$(this).stop(true).fadeIn()
}, function () {
$(this).stop(true).fadeOut()
});
$(".header_bar_box ul li").hover(function () {
$(this).css({
background: "#7A6E6E"
}).children(".div").css({
display: "block"
}).stop(true).animate({
left: "-60px"
}, 300)
}, function () {
$(this).css({
background: "#7A6E6E"
}).children(".div").css({
display: "none"
}).stop(true).animate({
left: "0"
}, 300)
});
$(".footer_foot .p1 a").hover(function () {
$(this).css("color", "#D70B1C")
}, function () {
$(this).css("color", "#727272")
});
$(".footer .footer_center ol li a").hover(function () {
$(this).css("color", "#D70B1C")
}, function () {
$(this).css("color", "#727272")
})
function searchProducts(name,value){
if (name == 'attrs'){
location.href = replaceParamVal(location.href,name,value,true)
}else{
location.href = replaceParamVal(location.href,name,value,false)
}
}
function searchByKeyword(){
searchProducts("keyword",$("#keyword").val()) ;
}
$(".page_a").click(function (){
let pn = $(this).attr("pn");
let href = location.href ;
if (href.indexOf("pageNum") != -1){
location.href = replaceParamVal(href,"pageNum",pn);
}else{
location.href = location.href + "&pageNum=" + pn;
}
return false;
})
function replaceParamVal(url,paramName,replaceVal,forceAdd){
var oUrl = url.toString();
if (oUrl.indexOf(paramName) != -1) {
if (forceAdd){
var nUrl = "";
if (oUrl.indexOf("?") != -1){
nUrl = oUrl + "&" + paramName + "=" + replaceVal;
}else{
nUrl = oUrl + "?" + paramName + "=" + replaceVal;
}
return nUrl;
}else{
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;
}
}
function jumpPage(){
let totalPage = [[${
result.totalPages}]];
let pn = $("#jumpPage").val();
if (pn > totalPage){
pn = totalPage;
}
let href = location.href ;
if (href.indexOf("pageNum") != -1){
location.href = replaceParamVal(href,"pageNum",pn);
}else{
location.href = location.href + "&pageNum=" + pn;
}
return false;
}
$(".sort_a").click(function (){
$(this).toggleClass("desc");
let sort = $(this).attr("sort");
sort = $(this).hasClass("desc")?sort+"_desc":sort+"_asc";
location.href = replaceParamVal(location.href,"sort",sort)
return false;
});
function changeStyle(ele){
$(".sort_a").css({
"color":"#333","border-color":"#CCC","background":"#FFF"});
$(ele).css({
"color":"#FFF","border-color":"#e4393c","background":"#e4393c"});
$(".sort_a").each(function (){
var text = $(this).text().replace("↓","").replace("↑","");
$(this).text(text);
})
$(ele).toggleClass("desc");
if ($(ele).hasClass("desc")){
var text = $(ele).text().replace("↓","").replace("↑","");
text = text + "↓";
$(ele).text(text);
}else{
var text = $(ele).text().replace("↓","").replace("↑","");
text = text + "↑";
$(ele).text(text);
}
}
$("#skuPriceSearchBtn").click(function (){
let from = $("#skuPriceFrom").val();
let to = $("#skuPriceTo").val();
let query = from + "_" + to;
location.href = replaceParamVal(location.href,"skuPrice",query);
})
$("#showHasStock").change(function (){
if( $(this).prop("checked") ) {
location.href = replaceParamVal(location.href,"hasStock",1);
} else {
let re = eval('/(hasStock=)([^&]*)/gi');
location.href = (location.href+"").replace(re,"");
}
return false;
})
</script>
</body>
</html>
package com.yxj.gulimall.search.service.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.yxj.common.to.es.SkuEsModel;
import com.yxj.common.utils.R;
import com.yxj.gulimall.search.config.GulimallElasticSearchConfig;
import com.yxj.gulimall.search.constant.EsConstant;
import com.yxj.gulimall.search.feign.ProductFeignService;
import com.yxj.gulimall.search.service.MallSearchService;
import com.yxj.gulimall.search.vo.AttrResponseVo;
import com.yxj.gulimall.search.vo.BrandVo;
import com.yxj.gulimall.search.vo.SearchParam;
import com.yxj.gulimall.search.vo.SearchResult;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@Service
public class MallSearchServiceImpl implements MallSearchService {
@Autowired
private RestHighLevelClient client;
@Autowired
private ProductFeignService productFeignService;
@Override
public SearchResult search(SearchParam param) {
SearchResult result = null;
SearchRequest searchRequest = buildSearchRequest(param);
try {
SearchResponse response = client.search(searchRequest, GulimallElasticSearchConfig.COMMON_OPTIONS);
result = buildSearchResult(response,param);
System.out.println(result.toString());
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
private SearchResult buildSearchResult(SearchResponse response,SearchParam param) {
SearchResult result = new SearchResult();
SearchHits hits = response.getHits();
List<SkuEsModel> list = 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);
}
list.add(esModel);
}
}
result.setProducts(list);
List<SearchResult.AttrVo> attrVos = new ArrayList<>();
ParsedNested attr_agg = response.getAggregations().get("attr_agg");
ParsedLongTerms attr_id_agg = attr_agg.getAggregations().get("attr_id_agg");
for (Terms.Bucket bucket : attr_id_agg.getBuckets()) {
SearchResult.AttrVo attrVo = new SearchResult.AttrVo();
long attrId = bucket.getKeyAsNumber().longValue();
attrVo.setAttrId(attrId);
String attrName = ((ParsedStringTerms) bucket.getAggregations().get("attr_name_agg")).getBuckets().get(0).getKeyAsString();
attrVo.setAttrName(attrName);
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.setAttrValue(attrValues);
attrVos.add(attrVo);
}
result.setAttrs(attrVos);
List<SearchResult.BrandVo> brands = new ArrayList<>();
ParsedLongTerms brand_agg = response.getAggregations().get("brand_agg");
List<? extends Terms.Bucket> brandAggBuckets = brand_agg.getBuckets();
for (Terms.Bucket bucket : brandAggBuckets) {
SearchResult.BrandVo brandVo = new SearchResult.BrandVo();
long brandId = bucket.getKeyAsNumber().longValue();
brandVo.setBrandId(brandId);
ParsedStringTerms brand_name_agg = bucket.getAggregations().get("brand_name_agg");
String brandName = brand_name_agg.getBuckets().get(0).getKeyAsString();
brandVo.setBrandName(brandName);
ParsedStringTerms brand_img_agg = bucket.getAggregations().get("brand_img_agg");
String brandImg = brand_img_agg.getBuckets().get(0).getKeyAsString();
brandVo.setBrandImg(brandImg);
brands.add(brandVo);
}
result.setBrands(brands);
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();
String keyAsString = bucket.getKeyAsString();
catalogVo.setCatalogId(Long.parseLong(keyAsString));
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.setCatalogs(catalogVos);
result.setPageNum(param.getPageNum());
long total = hits.getTotalHits().value;
result.setTotal(total);
int totalPages = 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);
if (param.getAttrs() !=null && param.getAttrs().size()>0){
List<SearchResult.NavVo> collect = param.getAttrs().stream().map(attr -> {
SearchResult.NavVo navVo = new SearchResult.NavVo();
String[] s = attr.split("_");
navVo.setNavValue(s[1]);
R r = productFeignService.attrInfo(Long.parseLong(s[0]));
result.getAttrIds().add(Long.parseLong(s[0]));
if (r.getCode() == 0){
AttrResponseVo attrResponseVo = (AttrResponseVo) r.getData("attr", new TypeReference<AttrResponseVo>() {
});
navVo.setNavName(attrResponseVo.getAttrName());
}else{
navVo.setNavName(s[0]);
}
String replace = replaceQueryString(param, attr,"attrs");
navVo.setLink("http://search.gulimail.com/list.html?"+replace);
return navVo;
}).collect(Collectors.toList());
result.setNavs(collect);
}
if (param.getBrandId() != null && param.getBrandId().size() >0){
List<SearchResult.NavVo> navs = result.getNavs();
SearchResult.NavVo navVo = new SearchResult.NavVo();
navVo.setNavName("品牌");
R r = productFeignService.brandsInfo(param.getBrandId());
if (r.getCode() == 0){
List<BrandVo> brand = (List<BrandVo>) 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.gulimail.com/list.html?"+replace);
}
navs.add(navVo);
result.setNavs(navs);
}
return result;
}
private String replaceQueryString(SearchParam param, String value, String key) {
String encode = null;
try {
encode = URLEncoder.encode(value, "UTF-8");
encode = encode.replace("+", "%20");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
String replace = param.get_queryString().replace("&"+key+"=" + encode, "");
return replace;
}
private SearchRequest buildSearchRequest(SearchParam param) {
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
if (!StringUtils.isEmpty(param.getKeyword())){
boolQuery.must(QueryBuilders.matchQuery("skuTitle",param.getKeyword()));
}
if (param.getCatalog3Id() != null){
boolQuery.filter(QueryBuilders.termQuery("catalogId", param.getCatalog3Id()));
}
if (param.getBrandId() != null && param.getBrandId().size()>0){
boolQuery.filter(QueryBuilders.termsQuery("brandId",param.getBrandId()));
}
if (param.getAttrs() != null && param.getAttrs().size()>0){
for (String attr : param.getAttrs()) {
BoolQueryBuilder nestedboolQuery = QueryBuilders.boolQuery();
String[] s = attr.split("_");
String attrId = s[0];
String[] attrValues = s[1].split(":");
nestedboolQuery.must(QueryBuilders.termQuery("attrs.attrId",attrId));
nestedboolQuery.must(QueryBuilders.termsQuery("attrs.attrValue",attrValues));
NestedQueryBuilder nestedQuery = QueryBuilders.nestedQuery("attrs", nestedboolQuery, ScoreMode.None);
boolQuery.filter(nestedQuery);
}
}
if (param.getHasStock() != null){
boolQuery.filter(QueryBuilders.termQuery("hasStock",param.getHasStock()==1));
}
if (!StringUtils.isEmpty(param.getSkuPrice())){
RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("skuPrice");
String[] s = param.getSkuPrice().split("_");
if (s.length == 2) {
rangeQuery.gte(s[0]).lte(s[1]);
} else if (s.length == 1) {
if (param.getSkuPrice().startsWith("_")){
rangeQuery.lte(s[0]);
}
if (param.getSkuPrice().endsWith("_")){
rangeQuery.gte(s[0]);
}
}
boolQuery.filter(rangeQuery);
}
sourceBuilder.query(boolQuery);
if (!StringUtils.isEmpty(param.getSort())){
String sort = param.getSort();
String[] s = sort.split("_");
SortOrder order = s[1].equalsIgnoreCase("asc")?SortOrder.ASC:SortOrder.DESC;
sourceBuilder.sort(s[0],order);
}
sourceBuilder.from((param.getPageNum()-1)*EsConstant.PRODUCT_PAGESIZE);
sourceBuilder.size(EsConstant.PRODUCT_PAGESIZE);
if (!StringUtils.isEmpty(param.getKeyword())){
HighlightBuilder builder = new HighlightBuilder();
builder.field("skuTitle");
builder.preTags("<b style='color:red'>");
builder.postTags("</b>");
sourceBuilder.highlighter(builder);
}
TermsAggregationBuilder brand_agg = AggregationBuilders.terms("brand_agg");
brand_agg.field("brandId").size(50);
brand_agg.subAggregation(AggregationBuilders.terms("brand_name_agg").field("brandName").size(1));
brand_agg.subAggregation(AggregationBuilders.terms("brand_img_agg").field("brandImg").size(1));
sourceBuilder.aggregation(brand_agg);
TermsAggregationBuilder catalog_agg = AggregationBuilders.terms("catalog_agg").field("catalogId").size(20);
catalog_agg.subAggregation(AggregationBuilders.terms("catalog_name_agg").field("catalogName").size(1));
sourceBuilder.aggregation(catalog_agg);
NestedAggregationBuilder attr_agg = AggregationBuilders.nested("attr_agg", "attrs");
TermsAggregationBuilder attr_id_agg = AggregationBuilders.terms("attr_id_agg").field("attrs.attrId");
attr_id_agg.subAggregation(AggregationBuilders.terms("attr_name_agg").field("attrs.attrName").size(1));
attr_id_agg.subAggregation(AggregationBuilders.terms("attr_value_agg").field("attrs.attrValue"));
attr_agg.subAggregation(attr_id_agg);
sourceBuilder.aggregation(attr_agg);
String s = sourceBuilder.toString();
System.out.println("构建的dsl语句:" + s);
SearchRequest searchRequest = new SearchRequest(new String[]{
EsConstant.PRODUCT_INDEX}, sourceBuilder);
return searchRequest;
}
}
2 异步编排
2.1 优化:异步编排
package com.yxj.gulimall.product.service.impl;
import com.yxj.gulimall.product.entity.SkuImagesEntity;
import com.yxj.gulimall.product.entity.SpuInfoDescEntity;
import com.yxj.gulimall.product.service.*;
import com.yxj.gulimall.product.vo.SkuItemSaleAttrVo;
import com.yxj.gulimall.product.vo.SkuItemVo;
import com.yxj.gulimall.product.vo.SpuItemAttrGroupVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yxj.common.utils.PageUtils;
import com.yxj.common.utils.Query;
import com.yxj.gulimall.product.dao.SkuInfoDao;
import com.yxj.gulimall.product.entity.SkuInfoEntity;
import org.springframework.util.StringUtils;
@Service("skuInfoService")
public class SkuInfoServiceImpl extends ServiceImpl<SkuInfoDao, SkuInfoEntity> implements SkuInfoService {
@Autowired
private SkuImagesService imagesService;
@Autowired
private SpuInfoDescService spuInfoDescService;
@Autowired
private AttrGroupService attrGroupService;
@Autowired
private SkuSaleAttrValueService skuSaleAttrValueService;
@Autowired
private ThreadPoolExecutor executor;
@Override
public PageUtils queryPage(Map<String, Object> params) {
IPage<SkuInfoEntity> page = this.page(
new Query<SkuInfoEntity>().getPage(params),
new QueryWrapper<SkuInfoEntity>()
);
return new PageUtils(page);
}
@Override
public void saveSkuInfo(SkuInfoEntity skuInfoEntity) {
this.baseMapper.insert(skuInfoEntity);
}
@Override
public PageUtils queryPageByCondition(Map<String, Object> params) {
QueryWrapper<SkuInfoEntity> queryWrapper = new QueryWrapper<>();
String key = (String) params.get("key");
if (!StringUtils.isEmpty(key)) {
queryWrapper.and((wrapper) ->{
wrapper.eq("sku_id", key).or().like("sku_name", key);
});
}
String catelogId = (String) params.get("catelogId");
if (!StringUtils.isEmpty(catelogId) && !"0".equalsIgnoreCase(catelogId)) {
queryWrapper.eq("catalog_id", catelogId);
}
String brandId = (String) params.get("brandId");
if (!StringUtils.isEmpty(brandId) && !"0".equalsIgnoreCase(catelogId)) {
queryWrapper.eq("brand_id", brandId);
}
String min = (String) params.get("min");
if (!StringUtils.isEmpty(min)) {
queryWrapper.ge("price", min);
}
String max = (String) params.get("max");
if (!StringUtils.isEmpty(max)) {
try {
BigDecimal bigDecimal = new BigDecimal(max);
if (bigDecimal.compareTo(new BigDecimal("0")) == 1) {
queryWrapper.le("price", max);
}
} catch (Exception e) {
}
queryWrapper.le("price", max);
}
IPage<SkuInfoEntity> page = this.page(
new Query<SkuInfoEntity>().getPage(params),queryWrapper
);
return new PageUtils(page);
}
@Override
public List<SkuInfoEntity> getSkusBySpuId(Long spuId) {
List<SkuInfoEntity> list = this.list(new QueryWrapper<SkuInfoEntity>().eq("spu_id",spuId));
return list;
}
@Override
public SkuItemVo item(Long skuId) {
SkuItemVo skuItemVo = new SkuItemVo();
CompletableFuture<SkuInfoEntity> infoFuture = CompletableFuture.supplyAsync(() -> {
SkuInfoEntity info = getById(skuId);
skuItemVo.setInfo(info);
return info;
}, executor);
CompletableFuture<Void> saleAttrFuture = infoFuture.thenAcceptAsync((res) -> {
List<SkuItemSaleAttrVo> saleAttrVos = skuSaleAttrValueService.getSaleAttrsBySpuId(res.getSpuId());
skuItemVo.setSaleAttr(saleAttrVos);
}, executor);
CompletableFuture<Void> descFuture = infoFuture.thenAcceptAsync(res -> {
SpuInfoDescEntity spuInfoDescEntity = spuInfoDescService.getById(res.getSpuId());
skuItemVo.setDesp(spuInfoDescEntity);
}, executor);
CompletableFuture<Void> attrGroupFuture = infoFuture.thenAcceptAsync(res -> {
List<SpuItemAttrGroupVo> attrGroupVos = attrGroupService.getAttrGroupWithAttrsBySpuId(res.getSpuId(), res.getCatalogId());
skuItemVo.setGroupAttrs(attrGroupVos);
}, executor);
CompletableFuture<Void> imgFuture = CompletableFuture.runAsync(() -> {
List<SkuImagesEntity> skuImagesEntityList = imagesService.getImagesBySkuId(skuId);
skuItemVo.setImages(skuImagesEntityList);
}, executor);
try {
CompletableFuture.allOf(saleAttrFuture,descFuture,attrGroupFuture,imgFuture).get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
return skuItemVo;
}
}